Module: Msf::Exploit::Remote::WinRM

Includes:
Exploit::Remote::NTLM::Client, HttpClient
Defined in:
lib/msf/core/exploit/winrm.rb

Constant Summary

Constants included from HttpClient

HttpClient::NTLM_CONST, HttpClient::NTLM_CRYPT, HttpClient::NTLM_UTILS, HttpClient::NTLM_XCEPT

Instance Method Summary collapse

Methods included from HttpClient

#basic_auth, #cleanup, #connect, #disconnect, #full_uri, #handler, #http_fingerprint, #make_cnonce, #normalize_uri, #path_from_uri, #peer, #proxies, #rhost, #rport, #send_request_cgi, #send_request_cgi!, #send_request_raw, #setup, #ssl, #ssl_version, #strip_tags, #target_uri, #validate_fingerprint, #vhost

Methods included from Auxiliary::Report

#db, #get_client, #get_host, #inside_workspace_boundary?, #mytask, #myworkspace, #report_auth_info, #report_client, #report_exploit, #report_host, #report_loot, #report_note, #report_service, #report_vuln, #report_web_form, #report_web_page, #report_web_site, #report_web_vuln, #store_cred, #store_local, #store_loot

Instance Method Details

#accepts_ntlm_authObject


209
210
211
# File 'lib/msf/core/exploit/winrm.rb', line 209

def accepts_ntlm_auth
  parse_auth_methods(winrm_poke).include? "Negotiate"
end

#generate_uuidObject


205
206
207
# File 'lib/msf/core/exploit/winrm.rb', line 205

def generate_uuid
  ::Rex::Proto::DCERPC::UUID.uuid_unpack(Rex::Text.rand_text(16))
end

#initialize(info = {}) ⇒ Object


21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/msf/core/exploit/winrm.rb', line 21

def initialize(info = {})
  super
  register_options(
    [
      Opt::RPORT(5985),
      OptString.new('DOMAIN', [ true, 'The domain to use for Windows authentification', 'WORKSTATION']),
      OptString.new('URI', [ true, "The URI of the WinRM service", "/wsman" ]),
      OptString.new('USERNAME', [ false, 'A specific username to authenticate as' ]),
      OptString.new('PASSWORD', [ false, 'A specific password to authenticate with' ]),
    ], self.class
  )

  register_autofilter_ports([ 80,443,5985,5986 ])
  register_autofilter_services(%W{ winrm })
end

#parse_auth_methods(resp) ⇒ Object


41
42
43
44
45
46
47
48
# File 'lib/msf/core/exploit/winrm.rb', line 41

def parse_auth_methods(resp)
  return [] unless resp and resp.code == 401
  methods = []
  methods << "Negotiate" if resp.headers['WWW-Authenticate'].include? "Negotiate"
  methods << "Kerberos" if resp.headers['WWW-Authenticate'].include? "Kerberos"
  methods << "Basic" if resp.headers['WWW-Authenticate'].include? "Basic"
  return methods
end

#parse_wql_response(response) ⇒ Object


151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/msf/core/exploit/winrm.rb', line 151

def parse_wql_response(response)
  return nil if response.nil?
  xml = response.body
  columns = []
  rows =[]
  rxml = REXML::Document.new(xml).root
  items = rxml.elements["///w:Items"]
  items.elements.to_a("///w:XmlFragment").each do |node|
    row_data = []
    node.elements.to_a.each do |sub_node|
      columns << sub_node.name
      row_data << sub_node.text
    end
    rows << row_data
  end
  columns.uniq!
  response_data = Rex::Ui::Text::Table.new(
    'Header'    => "#{datastore['WQL']} (#{rhost})",
    'Indent'    => 1,
    'Columns'   => columns
  )
  rows.each do |row|
    response_data << row
  end
  return response_data
end

#send_winrm_request(data, timeout = 20) ⇒ Object


231
232
233
234
235
236
237
238
239
240
241
# File 'lib/msf/core/exploit/winrm.rb', line 231

def send_winrm_request(data, timeout=20)
  opts = {
    'uri' => datastore['URI'],
    'method' => 'POST',
     'data' => data,
     'username' => datastore['USERNAME'],
    'password' => datastore['PASSWORD'],
    'ctype' => "application/soap+xml;charset=UTF-8"
  }
  send_request_cgi(opts,timeout)
end

#target_urlObject


213
214
215
216
217
218
219
220
221
222
223
# File 'lib/msf/core/exploit/winrm.rb', line 213

def target_url
  proto = "http"
  if rport == 5986 or datastore['SSL']
    proto = "https"
  end
  if datastore['VHOST']
    return  "#{proto}://#{datastore ['VHOST']}:#{rport}#{@uri.to_s}"
  else
    return "#{proto}://#{rhost}:#{rport}#{@uri.to_s}"
  end
end

#winrm_cmd_msg(cmd, shell_id) ⇒ Object


114
115
116
117
118
119
120
121
122
# File 'lib/msf/core/exploit/winrm.rb', line 114

def winrm_cmd_msg(cmd,shell_id)
  action = winrm_uri_action("send_cmd")
  options = winrm_option_set([['WINRS_CONSOLEMODE_STDIN', 'TRUE'], ['WINRS_SKIP_CMD_SHELL', 'FALSE']])
  selectors = winrm_selector_set([['ShellId', shell_id]])
  header_data = action + options + selectors
  contents = winrm_header(header_data) + winrm_cmd_body(cmd)
  msg = winrm_envelope(contents)
  return msg
end

#winrm_cmd_recv_msg(shell_id, cmd_id) ⇒ Object


124
125
126
127
128
129
130
131
# File 'lib/msf/core/exploit/winrm.rb', line 124

def winrm_cmd_recv_msg(shell_id,cmd_id)
  action = winrm_uri_action("recv_cmd")
  selectors = winrm_selector_set([['ShellId', shell_id]])
  header_data = action +  selectors
  contents = winrm_header(header_data) + winrm_cmd_recv_body(cmd_id)
  msg = winrm_envelope(contents)
  return msg
end

#winrm_delete_shell_msg(shell_id) ⇒ Object


142
143
144
145
146
147
148
149
# File 'lib/msf/core/exploit/winrm.rb', line 142

def winrm_delete_shell_msg(shell_id)
  action = winrm_uri_action("delete_shell")
  selectors = winrm_selector_set([['ShellId', shell_id]])
  header_data = action +  selectors
  contents = winrm_header(header_data) + winrm_empty_body
  msg = winrm_envelope(contents)
  return msg
end

#winrm_get_cmd_id(response) ⇒ Object


184
185
186
187
188
# File 'lib/msf/core/exploit/winrm.rb', line 184

def winrm_get_cmd_id(response)
  return nil if response.nil?
  xml = response.body
  cmd_id = REXML::Document.new(xml).elements["//rsp:CommandId"].text
end

#winrm_get_cmd_streams(response) ⇒ Object


190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/msf/core/exploit/winrm.rb', line 190

def winrm_get_cmd_streams(response)
  return nil if response.nil?
  streams = {
    'stdout' => '',
    'stderr'   => '',
  }
  xml = response.body
  rxml = REXML::Document.new(xml).root
  rxml.elements.to_a("//rsp:Stream").each do |node|
    next if node.text.nil?
    streams[node.attributes['Name']] << Rex::Text.decode_base64(node.text)
  end
  return streams
end

#winrm_get_shell_id(response) ⇒ Object


178
179
180
181
182
# File 'lib/msf/core/exploit/winrm.rb', line 178

def winrm_get_shell_id(response)
  return nil if response.nil?
  xml = response.body
  shell_id = REXML::Document.new(xml).elements["//w:Selector"].text
end

#winrm_open_shell_msgObject


105
106
107
108
109
110
111
112
# File 'lib/msf/core/exploit/winrm.rb', line 105

def winrm_open_shell_msg
  action = winrm_uri_action("create_shell")
  options = winrm_option_set([['WINRS_NOPROFILE', 'FALSE'], ['WINRS_CODEPAGE', '437']])
  header_data = action + options
  contents = winrm_header(header_data) + winrm_open_shell_body
  msg = winrm_envelope(contents)
  return msg
end

#winrm_poke(timeout = 20) ⇒ Object


37
38
39
# File 'lib/msf/core/exploit/winrm.rb', line 37

def winrm_poke(timeout = 20)
  send_winrm_request(Rex::Text.rand_text_alpha(8), timeout)
end

#winrm_run_cmd(cmd, timeout = 20) ⇒ Object


50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/msf/core/exploit/winrm.rb', line 50

def winrm_run_cmd(cmd, timeout=20)
  resp = send_winrm_request(winrm_open_shell_msg,timeout)
  if resp.nil?
    print_error "Recieved no reply from server"
    return nil
  end
  if resp.code == 401
    print_error "Login failure! Recheck supplied credentials."
    return resp .code
  end
  unless resp.code == 200
    print_error "Got unexpected response: \n #{resp.to_s}"
    retval = resp.code || 0
    return retval
  end
  shell_id = winrm_get_shell_id(resp)
  resp = send_winrm_request(winrm_cmd_msg(cmd, shell_id),timeout)
  cmd_id = winrm_get_cmd_id(resp)
  resp = send_winrm_request(winrm_cmd_recv_msg(shell_id,cmd_id),timeout)
  streams = winrm_get_cmd_streams(resp)
  resp = send_winrm_request(winrm_terminate_cmd_msg(shell_id,cmd_id),timeout)
  resp = send_winrm_request(winrm_delete_shell_msg(shell_id))
  return streams
end

#winrm_run_cmd_hanging(cmd, timeout = 20) ⇒ Object


75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/msf/core/exploit/winrm.rb', line 75

def winrm_run_cmd_hanging(cmd, timeout=20)
  resp = send_winrm_request(winrm_open_shell_msg,timeout)
  if resp.nil?
    print_error "Recieved no reply from server"
    return nil
  end
  if resp.code == 401
    print_error "Login failure! Recheck supplied credentials."
    return resp .code
  end
  unless resp.code == 200
    print_error "Got unexpected response: \n #{resp.to_s}"
    retval = resp.code || 0
    return retval
  end
  shell_id = winrm_get_shell_id(resp)
  resp = send_winrm_request(winrm_cmd_msg(cmd, shell_id),timeout)
  cmd_id = winrm_get_cmd_id(resp)
  resp = send_winrm_request(winrm_cmd_recv_msg(shell_id,cmd_id),timeout)
  streams = winrm_get_cmd_streams(resp)
  return streams
end

#winrm_terminate_cmd_msg(shell_id, cmd_id) ⇒ Object


133
134
135
136
137
138
139
140
# File 'lib/msf/core/exploit/winrm.rb', line 133

def winrm_terminate_cmd_msg(shell_id,cmd_id)
  action = winrm_uri_action("signal_shell")
  selectors = winrm_selector_set([['ShellId', shell_id]])
  header_data = action +  selectors
  contents = winrm_header(header_data) + winrm_terminate_cmd_body(cmd_id)
  msg = winrm_envelope(contents)
  return msg
end

#winrm_wql_msg(wql) ⇒ Object


98
99
100
101
102
103
# File 'lib/msf/core/exploit/winrm.rb', line 98

def winrm_wql_msg(wql)
  action = winrm_uri_action("wql")
  contents = winrm_header(action) + winrm_wql_body(wql)
  msg = winrm_envelope(contents)
  return msg
end

#wmi_namespaceObject


225
226
227
228
229
# File 'lib/msf/core/exploit/winrm.rb', line 225

def wmi_namespace
  return datastore['NAMESPACE'] if datastore['NAMESPACE']
  return @namespace_override if @namespace_override
  return "/root/cimv2/"
end