Mirror of metasploit
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

winrm.rb 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. # -*- coding: binary -*-
  2. require 'uri'
  3. require 'digest'
  4. require 'rex/proto/ntlm/crypt'
  5. require 'rex/proto/ntlm/constants'
  6. require 'rex/proto/ntlm/utils'
  7. require 'rex/proto/ntlm/exceptions'
  8. module Msf
  9. module Exploit::Remote::WinRM
  10. include Exploit::Remote::NTLM::Client
  11. include Exploit::Remote::HttpClient
  12. #
  13. # Constants
  14. #
  15. NTLM_CRYPT ||= Rex::Proto::NTLM::Crypt
  16. NTLM_CONST ||= Rex::Proto::NTLM::Constants
  17. NTLM_UTILS ||= Rex::Proto::NTLM::Utils
  18. NTLM_XCEPT ||= Rex::Proto::NTLM::Exceptions
  19. def initialize(info = {})
  20. super
  21. register_options(
  22. [
  23. Opt::RPORT(5985),
  24. OptString.new('DOMAIN', [ true, 'The domain to use for Windows authentification', 'WORKSTATION']),
  25. OptString.new('URI', [ true, "The URI of the WinRM service", "/wsman" ]),
  26. OptString.new('USERNAME', [ false, 'A specific username to authenticate as' ]),
  27. OptString.new('PASSWORD', [ false, 'A specific password to authenticate with' ]),
  28. ], self.class
  29. )
  30. register_autofilter_ports([ 80,443,5985,5986 ])
  31. register_autofilter_services(%W{ winrm })
  32. end
  33. def winrm_poke(timeout = 20)
  34. send_winrm_request(Rex::Text.rand_text_alpha(8), timeout)
  35. end
  36. def parse_auth_methods(resp)
  37. return [] unless resp and resp.code == 401
  38. methods = []
  39. methods << "Negotiate" if resp.headers['WWW-Authenticate'].include? "Negotiate"
  40. methods << "Kerberos" if resp.headers['WWW-Authenticate'].include? "Kerberos"
  41. methods << "Basic" if resp.headers['WWW-Authenticate'].include? "Basic"
  42. return methods
  43. end
  44. def winrm_run_cmd(cmd, timeout=20)
  45. resp = send_winrm_request(winrm_open_shell_msg,timeout)
  46. if resp.nil?
  47. print_error "Recieved no reply from server"
  48. return nil
  49. end
  50. if resp.code == 401
  51. print_error "Login failure! Recheck supplied credentials."
  52. return resp .code
  53. end
  54. unless resp.code == 200
  55. print_error "Got unexpected response: \n #{resp.to_s}"
  56. retval = resp.code || 0
  57. return retval
  58. end
  59. shell_id = winrm_get_shell_id(resp)
  60. resp = send_winrm_request(winrm_cmd_msg(cmd, shell_id),timeout)
  61. cmd_id = winrm_get_cmd_id(resp)
  62. resp = send_winrm_request(winrm_cmd_recv_msg(shell_id,cmd_id),timeout)
  63. streams = winrm_get_cmd_streams(resp)
  64. resp = send_winrm_request(winrm_terminate_cmd_msg(shell_id,cmd_id),timeout)
  65. resp = send_winrm_request(winrm_delete_shell_msg(shell_id))
  66. return streams
  67. end
  68. def winrm_run_cmd_hanging(cmd, timeout=20)
  69. resp = send_winrm_request(winrm_open_shell_msg,timeout)
  70. if resp.nil?
  71. print_error "Recieved no reply from server"
  72. return nil
  73. end
  74. if resp.code == 401
  75. print_error "Login failure! Recheck supplied credentials."
  76. return resp .code
  77. end
  78. unless resp.code == 200
  79. print_error "Got unexpected response: \n #{resp.to_s}"
  80. retval = resp.code || 0
  81. return retval
  82. end
  83. shell_id = winrm_get_shell_id(resp)
  84. resp = send_winrm_request(winrm_cmd_msg(cmd, shell_id),timeout)
  85. cmd_id = winrm_get_cmd_id(resp)
  86. resp = send_winrm_request(winrm_cmd_recv_msg(shell_id,cmd_id),timeout)
  87. streams = winrm_get_cmd_streams(resp)
  88. return streams
  89. end
  90. def winrm_wql_msg(wql)
  91. action = winrm_uri_action("wql")
  92. contents = winrm_header(action) + winrm_wql_body(wql)
  93. msg = winrm_envelope(contents)
  94. return msg
  95. end
  96. def winrm_open_shell_msg
  97. action = winrm_uri_action("create_shell")
  98. options = winrm_option_set([['WINRS_NOPROFILE', 'FALSE'], ['WINRS_CODEPAGE', '437']])
  99. header_data = action + options
  100. contents = winrm_header(header_data) + winrm_open_shell_body
  101. msg = winrm_envelope(contents)
  102. return msg
  103. end
  104. def winrm_cmd_msg(cmd,shell_id)
  105. action = winrm_uri_action("send_cmd")
  106. options = winrm_option_set([['WINRS_CONSOLEMODE_STDIN', 'TRUE'], ['WINRS_SKIP_CMD_SHELL', 'FALSE']])
  107. selectors = winrm_selector_set([['ShellId', shell_id]])
  108. header_data = action + options + selectors
  109. contents = winrm_header(header_data) + winrm_cmd_body(cmd)
  110. msg = winrm_envelope(contents)
  111. return msg
  112. end
  113. def winrm_cmd_recv_msg(shell_id,cmd_id)
  114. action = winrm_uri_action("recv_cmd")
  115. selectors = winrm_selector_set([['ShellId', shell_id]])
  116. header_data = action + selectors
  117. contents = winrm_header(header_data) + winrm_cmd_recv_body(cmd_id)
  118. msg = winrm_envelope(contents)
  119. return msg
  120. end
  121. def winrm_terminate_cmd_msg(shell_id,cmd_id)
  122. action = winrm_uri_action("signal_shell")
  123. selectors = winrm_selector_set([['ShellId', shell_id]])
  124. header_data = action + selectors
  125. contents = winrm_header(header_data) + winrm_terminate_cmd_body(cmd_id)
  126. msg = winrm_envelope(contents)
  127. return msg
  128. end
  129. def winrm_delete_shell_msg(shell_id)
  130. action = winrm_uri_action("delete_shell")
  131. selectors = winrm_selector_set([['ShellId', shell_id]])
  132. header_data = action + selectors
  133. contents = winrm_header(header_data) + winrm_empty_body
  134. msg = winrm_envelope(contents)
  135. return msg
  136. end
  137. def parse_wql_response(response)
  138. return nil if response.nil?
  139. xml = response.body
  140. columns = []
  141. rows =[]
  142. rxml = REXML::Document.new(xml).root
  143. items = rxml.elements["///w:Items"]
  144. items.elements.to_a("///w:XmlFragment").each do |node|
  145. row_data = []
  146. node.elements.to_a.each do |sub_node|
  147. columns << sub_node.name
  148. row_data << sub_node.text
  149. end
  150. rows << row_data
  151. end
  152. columns.uniq!
  153. response_data = Rex::Text::Table.new(
  154. 'Header' => "#{datastore['WQL']} (#{rhost})",
  155. 'Indent' => 1,
  156. 'Columns' => columns
  157. )
  158. rows.each do |row|
  159. response_data << row
  160. end
  161. return response_data
  162. end
  163. def winrm_get_shell_id(response)
  164. return nil if response.nil?
  165. xml = response.body
  166. shell_id = REXML::Document.new(xml).elements["//w:Selector"].text
  167. end
  168. def winrm_get_cmd_id(response)
  169. return nil if response.nil?
  170. xml = response.body
  171. cmd_id = REXML::Document.new(xml).elements["//rsp:CommandId"].text
  172. end
  173. def winrm_get_cmd_streams(response)
  174. return nil if response.nil?
  175. streams = {
  176. 'stdout' => '',
  177. 'stderr' => '',
  178. }
  179. xml = response.body
  180. rxml = REXML::Document.new(xml).root
  181. rxml.elements.to_a("//rsp:Stream").each do |node|
  182. next if node.text.nil?
  183. streams[node.attributes['Name']] << Rex::Text.decode_base64(node.text)
  184. end
  185. return streams
  186. end
  187. def generate_uuid
  188. ::Rex::Proto::DCERPC::UUID.uuid_unpack(Rex::Text.rand_text(16))
  189. end
  190. def accepts_ntlm_auth
  191. parse_auth_methods(winrm_poke).include? "Negotiate"
  192. end
  193. def target_url
  194. proto = "http"
  195. if rport == 5986 or datastore['SSL']
  196. proto = "https"
  197. end
  198. if datastore['VHOST']
  199. return "#{proto}://#{datastore ['VHOST']}:#{rport}#{@uri.to_s}"
  200. else
  201. return "#{proto}://#{rhost}:#{rport}#{@uri.to_s}"
  202. end
  203. end
  204. def wmi_namespace
  205. return datastore['NAMESPACE'] if datastore['NAMESPACE']
  206. return @namespace_override if @namespace_override
  207. return "/root/cimv2/"
  208. end
  209. def send_winrm_request(data, timeout=20)
  210. opts = {
  211. 'uri' => datastore['URI'],
  212. 'method' => 'POST',
  213. 'data' => data,
  214. 'username' => datastore['USERNAME'],
  215. 'password' => datastore['PASSWORD'],
  216. 'ctype' => "application/soap+xml;charset=UTF-8"
  217. }
  218. send_request_cgi(opts,timeout)
  219. end
  220. private
  221. def winrm_option_set(options)
  222. xml = "<w:OptionSet>"
  223. options.each do |option_pair|
  224. xml << winrm_option(*option_pair)
  225. end
  226. xml << "</w:OptionSet>"
  227. return xml
  228. end
  229. def winrm_option(name,value)
  230. %Q{<w:Option Name="#{name}">#{value}</w:Option>}
  231. end
  232. def winrm_selector_set(selectors)
  233. xml = "<w:SelectorSet>"
  234. selectors.each do |selector_pair|
  235. xml << winrm_selector(*selector_pair)
  236. end
  237. xml << "</w:SelectorSet>"
  238. return xml
  239. end
  240. def winrm_selector(name,value)
  241. %Q{<w:Selector Name="#{name}">#{value}</w:Selector>}
  242. end
  243. def winrm_wql_body(wql)
  244. %Q{
  245. <env:Body>
  246. <n:Enumerate>
  247. <w:OptimizeEnumeration xsi:nil="true"/>
  248. <w:MaxElements>32000</w:MaxElements>
  249. <w:Filter Dialect="http://schemas.microsoft.com/wbem/wsman/1/WQL">#{wql}</w:Filter>
  250. </n:Enumerate>
  251. </env:Body>
  252. }
  253. end
  254. def winrm_open_shell_body
  255. %q{<env:Body>
  256. <rsp:Shell>
  257. <rsp:InputStreams>stdin</rsp:InputStreams>
  258. <rsp:OutputStreams>stdout stderr</rsp:OutputStreams>
  259. </rsp:Shell>
  260. </env:Body>}
  261. end
  262. def winrm_cmd_body(cmd)
  263. %Q{ <env:Body>
  264. <rsp:CommandLine>
  265. <rsp:Command>&quot;#{cmd}&quot;</rsp:Command>
  266. </rsp:CommandLine>
  267. </env:Body>}
  268. end
  269. def winrm_cmd_recv_body(cmd_id)
  270. %Q{<env:Body>
  271. <rsp:Receive>
  272. <rsp:DesiredStream CommandId="#{cmd_id}">stdout stderr</rsp:DesiredStream>
  273. </rsp:Receive>
  274. </env:Body>}
  275. end
  276. def winrm_terminate_cmd_body(cmd_id)
  277. %Q{ <env:Body>
  278. <rsp:Signal CommandId="#{cmd_id}">
  279. <rsp:Code>http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/terminate</rsp:Code>
  280. </rsp:Signal>
  281. </env:Body>}
  282. end
  283. def winrm_empty_body
  284. %q{<env:Body/>}
  285. end
  286. def winrm_envelope(data)
  287. %Q{
  288. <?xml version="1.0" encoding="UTF-8"?>
  289. <env:Envelope xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:b="http://schemas.dmtf.org/wbem/wsman/1/cimbinding.xsd"
  290. xmlns:cfg="http://schemas.microsoft.com/wbem/wsman/1/config" xmlns:env="http://www.w3.org/2003/05/soap-envelope"
  291. xmlns:n="http://schemas.xmlsoap.org/ws/2004/09/enumeration" xmlns:p="http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd"
  292. xmlns:rsp="http://schemas.microsoft.com/wbem/wsman/1/windows/shell" xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd"
  293. xmlns:x="http://schemas.xmlsoap.org/ws/2004/09/transfer" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  294. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> #{data}</env:Envelope>
  295. }
  296. end
  297. def winrm_header(data)
  298. %Q{
  299. <env:Header>
  300. <a:To>#{target_url}</a:To>
  301. <a:ReplyTo>
  302. <a:Address mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address>
  303. </a:ReplyTo>
  304. <w:MaxEnvelopeSize mustUnderstand="true">153600</w:MaxEnvelopeSize>
  305. <a:MessageID>uuid:#{generate_uuid}</a:MessageID>
  306. <w:Locale mustUnderstand="false" xml:lang="en-US"/>
  307. <p:DataLocale mustUnderstand="false" xml:lang="en-US"/>
  308. <w:OperationTimeout>PT60S</w:OperationTimeout>
  309. #{data}
  310. </env:Header>
  311. }
  312. end
  313. def winrm_uri_action(type)
  314. case type
  315. when "wql"
  316. return %Q{<w:ResourceURI mustUnderstand="true">http://schemas.microsoft.com/wbem/wsman/1/wmi#{wmi_namespace}*</w:ResourceURI>
  317. <a:Action mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/09/enumeration/Enumerate</a:Action>}
  318. when "create_shell"
  319. return %q{<w:ResourceURI mustUnderstand="true">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd</w:ResourceURI>
  320. <a:Action mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/09/transfer/Create</a:Action>}
  321. when "send_cmd"
  322. return %q{<w:ResourceURI mustUnderstand="true">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd</w:ResourceURI>
  323. <a:Action mustUnderstand="true">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Command</a:Action>}
  324. when "recv_cmd"
  325. return %q{<w:ResourceURI mustUnderstand="true">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd</w:ResourceURI>
  326. <a:Action mustUnderstand="true">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive</a:Action>}
  327. when "signal_shell"
  328. return %q{<w:ResourceURI mustUnderstand="true">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd</w:ResourceURI>
  329. <a:Action mustUnderstand="true">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Signal</a:Action>}
  330. when "delete_shell"
  331. return %q{<w:ResourceURI mustUnderstand="true">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd</w:ResourceURI>
  332. <a:Action mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete</a:Action>}
  333. end
  334. end
  335. end
  336. end