8
8
9
9
module Msf
10
10
module Exploit ::Remote ::WinRM
11
-
12
11
include Exploit ::Remote ::NTLM ::Client
13
12
include Exploit ::Remote ::HttpClient
14
-
15
13
#
16
14
# Constants
17
15
#
18
16
NTLM_CRYPT ||= Rex ::Proto ::NTLM ::Crypt
19
17
NTLM_CONST ||= Rex ::Proto ::NTLM ::Constants
20
18
NTLM_UTILS ||= Rex ::Proto ::NTLM ::Utils
21
19
NTLM_XCEPT ||= Rex ::Proto ::NTLM ::Exceptions
22
-
23
20
def initialize ( info = { } )
24
21
super
25
22
register_options (
26
23
[
27
- Opt ::RHOST ,
28
24
Opt ::RPORT ( 5985 ) ,
29
- OptString . new ( 'VHOST' , [ false , "HTTP server virtual host" ] ) ,
30
- OptBool . new ( 'SSL' , [ false , 'Negotiate SSL for outgoing connections' , false ] ) ,
31
- OptEnum . new ( 'SSLVersion' , [ false , 'Specify the version of SSL that should be used' , 'SSL3' , [ 'SSL2' , 'SSL3' , 'TLS1' ] ] ) ,
32
25
OptString . new ( 'DOMAIN' , [ true , 'The domain to use for Windows authentification' , 'WORKSTATION' ] ) ,
33
26
OptString . new ( 'URI' , [ true , "The URI of the WinRM service" , "/wsman" ] ) ,
34
27
OptString . new ( 'USERNAME' , [ false , 'A specific username to authenticate as' ] ) ,
35
- OptString . new ( 'PASSWORD' , [ false , 'A specific password to authenticate with' ] )
28
+ OptString . new ( 'PASSWORD' , [ false , 'A specific password to authenticate with' ] ) ,
36
29
] , self . class
37
30
)
38
31
@@ -45,18 +38,15 @@ def winrm_poke(timeout = 20)
45
38
'uri' => datastore [ 'URI' ] ,
46
39
'data' => Rex ::Text . rand_text_alpha ( 8 )
47
40
}
48
-
49
- c = connect ( opts )
50
- to = opts [ :timeout ] || timeout
41
+ c = connect ( opts )
42
+ to = opts [ :timeout ] || timeout
51
43
ctype = "application/soap+xml;charset=UTF-8"
52
-
53
44
resp , c = send_request_cgi ( opts . merge ( {
54
- 'uri' => opts [ 'uri' ] ,
45
+ 'uri' => opts [ 'uri' ] ,
55
46
'method' => 'POST' ,
56
- 'ctype' => ctype ,
57
- 'data' => opts [ 'data' ]
47
+ 'ctype' => ctype ,
48
+ 'data' => opts [ 'data' ]
58
49
} ) , to )
59
-
60
50
return resp
61
51
end
62
52
@@ -71,34 +61,29 @@ def parse_auth_methods(resp)
71
61
72
62
def winrm_run_cmd ( cmd , timeout = 20 )
73
63
resp , c = send_request_ntlm ( winrm_open_shell_msg , timeout )
74
-
75
64
if resp . code == 401
76
65
print_error "Login failure! Recheck supplied credentials."
77
66
return resp . code
78
67
end
79
-
80
68
unless resp . code == 200
81
69
print_error "Got unexpected response: \n #{ resp . to_s } "
82
70
retval == resp . code || 0
83
71
return retval
84
72
end
85
-
86
73
shell_id = winrm_get_shell_id ( resp )
87
74
resp , c = send_request_ntlm ( winrm_cmd_msg ( cmd , shell_id ) , timeout )
88
75
cmd_id = winrm_get_cmd_id ( resp )
89
76
resp , c = send_request_ntlm ( winrm_cmd_recv_msg ( shell_id , cmd_id ) , timeout )
90
77
streams = winrm_get_cmd_streams ( resp )
91
78
resp , c = send_request_ntlm ( winrm_terminate_cmd_msg ( shell_id , cmd_id ) , timeout )
92
79
resp , c = send_request_ntlm ( winrm_delete_shell_msg ( shell_id ) )
93
-
94
80
return streams
95
81
end
96
82
97
83
def winrm_wql_msg ( wql )
98
84
action = winrm_uri_action ( "wql" )
99
85
contents = winrm_header ( action ) + winrm_wql_body ( wql )
100
86
msg = winrm_envelope ( contents )
101
-
102
87
return msg
103
88
end
104
89
@@ -108,7 +93,6 @@ def winrm_open_shell_msg
108
93
header_data = action + options
109
94
contents = winrm_header ( header_data ) + winrm_open_shell_body
110
95
msg = winrm_envelope ( contents )
111
-
112
96
return msg
113
97
end
114
98
@@ -119,7 +103,6 @@ def winrm_cmd_msg(cmd,shell_id)
119
103
header_data = action + options + selectors
120
104
contents = winrm_header ( header_data ) + winrm_cmd_body ( cmd )
121
105
msg = winrm_envelope ( contents )
122
-
123
106
return msg
124
107
end
125
108
@@ -129,7 +112,6 @@ def winrm_cmd_recv_msg(shell_id,cmd_id)
129
112
header_data = action + selectors
130
113
contents = winrm_header ( header_data ) + winrm_cmd_recv_body ( cmd_id )
131
114
msg = winrm_envelope ( contents )
132
-
133
115
return msg
134
116
end
135
117
@@ -139,7 +121,6 @@ def winrm_terminate_cmd_msg(shell_id,cmd_id)
139
121
header_data = action + selectors
140
122
contents = winrm_header ( header_data ) + winrm_terminate_cmd_body ( cmd_id )
141
123
msg = winrm_envelope ( contents )
142
-
143
124
return msg
144
125
end
145
126
@@ -149,7 +130,6 @@ def winrm_delete_shell_msg(shell_id)
149
130
header_data = action + selectors
150
131
contents = winrm_header ( header_data ) + winrm_empty_body
151
132
msg = winrm_envelope ( contents )
152
-
153
133
return msg
154
134
end
155
135
@@ -159,28 +139,23 @@ def parse_wql_response(response)
159
139
rows = [ ]
160
140
rxml = REXML ::Document . new ( xml ) . root
161
141
items = rxml . elements [ "///w:Items" ]
162
-
163
142
items . elements . to_a ( "///w:XmlFragment" ) . each do |node |
164
143
row_data = [ ]
165
-
166
144
node . elements . to_a . each do |sub_node |
167
145
columns << sub_node . name
168
146
row_data << sub_node . text
169
147
end
170
-
171
148
rows << row_data
172
149
end
173
-
150
+ columns . uniq!
174
151
response_data = Rex ::Ui ::Text ::Table . new (
175
152
'Header' => "#{ datastore [ 'WQL' ] } (#{ rhost } )" ,
176
153
'Indent' => 1 ,
177
- 'Columns' => columns . uniq!
154
+ 'Columns' => columns
178
155
)
179
-
180
156
rows . each do |row |
181
157
response_data << row
182
158
end
183
-
184
159
return response_data
185
160
end
186
161
@@ -197,17 +172,14 @@ def winrm_get_cmd_id(response)
197
172
def winrm_get_cmd_streams ( response )
198
173
streams = {
199
174
'stdout' => '' ,
200
- 'stderr' => '' ,
175
+ 'stderr' => '' ,
201
176
}
202
-
203
177
xml = response . body
204
178
rxml = REXML ::Document . new ( xml ) . root
205
-
206
179
rxml . elements . to_a ( "//rsp:Stream" ) . each do |node |
207
180
next if node . text . nil?
208
181
streams [ node . attributes [ 'Name' ] ] << Rex ::Text . base64_decode ( node . text )
209
182
end
210
-
211
183
return streams
212
184
end
213
185
@@ -222,25 +194,20 @@ def send_request_ntlm(data, timeout = 20)
222
194
'username' => datastore [ 'USERNAME' ] ,
223
195
'password' => datastore [ 'PASSWORD' ]
224
196
}
225
-
226
- ntlm_options =
227
- {
228
- :signing => false ,
229
- :usentlm2_session => datastore [ 'NTLM::UseNTLM2_session' ] ,
230
- :use_ntlmv2 => datastore [ 'NTLM::UseNTLMv2' ] ,
231
- :send_lm => datastore [ 'NTLM::SendLM' ] ,
232
- :send_ntlm => datastore [ 'NTLM::SendNTLM' ]
233
- }
234
-
197
+ ntlm_options = {
198
+ :signing => false ,
199
+ :usentlm2_session => datastore [ 'NTLM::UseNTLM2_session' ] ,
200
+ :use_ntlmv2 => datastore [ 'NTLM::UseNTLMv2' ] ,
201
+ :send_lm => datastore [ 'NTLM::SendLM' ] ,
202
+ :send_ntlm => datastore [ 'NTLM::SendNTLM' ]
203
+ }
235
204
ntlmssp_flags = NTLM_UTILS . make_ntlm_flags ( ntlm_options )
236
205
workstation_name = Rex ::Text . rand_text_alpha ( rand ( 8 ) +1 )
237
206
domain_name = datastore [ 'DOMAIN' ]
238
207
ntlm_message_1 = "NEGOTIATE " + Rex ::Text ::encode_base64 ( NTLM_UTILS ::make_ntlmssp_blob_init ( domain_name ,
239
208
workstation_name ,
240
209
ntlmssp_flags ) )
241
-
242
210
to = opts [ :timeout ] || timeout
243
-
244
211
begin
245
212
c = connect ( opts )
246
213
ctype = "application/soap+xml;charset=UTF-8"
@@ -251,14 +218,11 @@ def send_request_ntlm(data, timeout = 20)
251
218
'ctype' => ctype ,
252
219
'headers' => { 'Authorization' => ntlm_message_1 } ,
253
220
'data' => opts [ 'data' ]
254
- } ) )
255
-
221
+ } ) )
256
222
resp = c . send_recv ( r , to )
257
-
258
223
unless resp . kind_of? Rex ::Proto ::Http ::Response
259
224
return [ nil , nil ]
260
225
end
261
-
262
226
return [ nil , nil ] if resp . code == 404
263
227
return [ nil , nil ] unless resp . code == 401 && resp . headers [ 'WWW-Authenticate' ]
264
228
# Get the challenge and craft the response
@@ -293,7 +257,6 @@ def send_request_ntlm(data, timeout = 20)
293
257
ntlm_message_3 = NTLM_UTILS . make_ntlmssp_blob_auth ( domain_name , workstation_name , opts [ 'username' ] ,
294
258
resp_lm , resp_ntlm , '' , ntlmssp_flags )
295
259
ntlm_message_3 = Rex ::Text ::encode_base64 ( ntlm_message_3 )
296
-
297
260
# Send the response
298
261
r = c . request_cgi ( opts . merge ( {
299
262
'uri' => opts [ 'uri' ] ,
@@ -302,13 +265,10 @@ def send_request_ntlm(data, timeout = 20)
302
265
'headers' => { 'Authorization' => "NEGOTIATE #{ ntlm_message_3 } " } ,
303
266
'data' => opts [ 'data' ]
304
267
} ) )
305
-
306
268
resp = c . send_recv ( r , to , true )
307
-
308
269
unless resp . kind_of? Rex ::Proto ::Http ::Response
309
270
return [ nil , nil ]
310
271
end
311
-
312
272
return [ nil , nil ] if resp . code == 404
313
273
return [ resp , c ]
314
274
rescue ::Errno ::EPIPE , ::Timeout ::Error
@@ -324,25 +284,20 @@ def target_url
324
284
if rport == 5986 or datastore [ 'SSL' ]
325
285
proto = "https"
326
286
end
327
-
328
287
if datastore [ 'VHOST' ]
329
288
return "#{ proto } ://#{ datastore [ 'VHOST' ] } :#{ rport } #{ @uri . to_s } "
330
289
else
331
290
return "#{ proto } ://#{ rhost } :#{ rport } #{ @uri . to_s } "
332
291
end
333
292
end
334
293
335
-
336
-
337
294
private
338
295
339
296
def winrm_option_set ( options )
340
297
xml = "<w:OptionSet>"
341
-
342
298
options . each do |option_pair |
343
299
xml << winrm_option ( *option_pair )
344
300
end
345
-
346
301
xml << "</w:OptionSet>"
347
302
return xml
348
303
end
@@ -353,11 +308,9 @@ def winrm_option(name,value)
353
308
354
309
def winrm_selector_set ( selectors )
355
310
xml = "<w:SelectorSet>"
356
-
357
311
selectors . each do |selector_pair |
358
312
xml << winrm_selector ( *selector_pair )
359
313
end
360
-
361
314
xml << "</w:SelectorSet>"
362
315
return xml
363
316
end
0 commit comments