msfcli 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. #!/usr/bin/env ruby
  2. # -*- coding: binary -*-
  3. #
  4. # This user interface allows users to interact with the framework through a
  5. # command line interface (CLI) rather than having to use a prompting console
  6. # or web-based interface.
  7. #
  8. $stderr.puts "[!] ************************************************************************"
  9. $stderr.puts "[!] * The utility msfcli is deprecated! *"
  10. $stderr.puts "[!] * It will be removed on or about 2015-06-18 *"
  11. $stderr.puts "[!] * Please use msfconsole -r or -x instead *"
  12. $stderr.puts "[!] * Details: https://github.com/rapid7/metasploit-framework/pull/3802 *"
  13. $stderr.puts "[!] ************************************************************************"
  14. msfbase = __FILE__
  15. while File.symlink?(msfbase)
  16. msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
  17. end
  18. $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib')))
  19. require 'rex'
  20. class Msfcli
  21. #
  22. # Attributes
  23. #
  24. # @attribute framework
  25. # @return [Msf::Simple::Framework]
  26. #
  27. # initialize
  28. #
  29. def initialize(args)
  30. @args = {}
  31. @indent = ' '
  32. @args[:module_name] = args.shift # First argument should be the module name
  33. @args[:mode] = args.pop || 'h' # Last argument should be the mode
  34. @args[:params] = args # Whatever is in the middle should be the params
  35. if @args[:module_name] =~ /^exploit(s)*\//i
  36. @args[:module_name] = @args[:module_name].split('/')
  37. @args[:module_name] = @args[:module_name][1, @args[:module_name].length] * "/"
  38. end
  39. end
  40. #
  41. # Instance Methods
  42. #
  43. # The framework to create and list modules.
  44. #
  45. # @return [Msf::Simple::Framework]
  46. def framework
  47. @framework ||= Msf::Simple::Framework.create({'DeferModuleLoads'=>true})
  48. end
  49. # Sets {#framework}.
  50. #
  51. # @raise [ArgumentError] if {#framework} already set
  52. def framework=(framework)
  53. if instance_variable_defined? :@framework
  54. fail ArgumentError.new("framework already set")
  55. end
  56. @framework = framework
  57. end
  58. #
  59. # Returns a usage Rex table
  60. #
  61. def usage (str = nil, extra = nil)
  62. tbl = Rex::Ui::Text::Table.new(
  63. 'Header' => "Usage: #{$0} <exploit_name> <option=value> [mode]",
  64. 'Indent' => 4,
  65. 'Columns' => ['Mode', 'Description']
  66. )
  67. tbl << ['(H)elp', "You're looking at it baby!"]
  68. tbl << ['(S)ummary', 'Show information about this module']
  69. tbl << ['(O)ptions', 'Show available options for this module']
  70. tbl << ['(M)issing', 'Show empty required options for this module']
  71. tbl << ['(A)dvanced', 'Show available advanced options for this module']
  72. tbl << ['(I)DS Evasion', 'Show available ids evasion options for this module']
  73. tbl << ['(P)ayloads', 'Show available payloads for this module']
  74. tbl << ['(T)argets', 'Show available targets for this exploit module']
  75. tbl << ['(AC)tions', 'Show available actions for this module']
  76. tbl << ['(C)heck', 'Run the check routine of the selected module']
  77. tbl << ['(E)xecute', 'Execute the selected module']
  78. tbl.to_s
  79. $stdout.puts "Error: #{str}\n\n" if str
  80. $stdout.puts tbl.to_s + "\n"
  81. $stdout.puts "Examples:" + "\n"
  82. $stdout.puts "msfcli multi/handler payload=windows/meterpreter/reverse_tcp lhost=IP E" + "\n"
  83. $stdout.puts "msfcli auxiliary/scanner/http/http_version rhosts=IP encoder= post= nop= E" + "\n"
  84. $stdout.puts extra + "\n" if extra
  85. $stdout.puts
  86. end
  87. #
  88. # Loads up everything in framework, and then returns the module list
  89. #
  90. def dump_module_list
  91. # This is what happens if the user doesn't specify a module name:
  92. # msfcli will end up loading EVERYTHING to memory to show you a help
  93. # menu plus a list of modules available. Really expensive if you ask me.
  94. $stdout.puts "[*] Please wait while we load the module tree..."
  95. self.framework = Msf::Simple::Framework.create
  96. ext = ''
  97. tbl = Rex::Ui::Text::Table.new(
  98. 'Header' => 'Exploits',
  99. 'Indent' => 4,
  100. 'Columns' => [ 'Name', 'Description' ])
  101. framework.exploits.each_module { |name, mod|
  102. tbl << [ 'exploit/' + name, mod.new.name ]
  103. }
  104. ext << tbl.to_s + "\n"
  105. tbl = Rex::Ui::Text::Table.new(
  106. 'Header' => 'Auxiliary',
  107. 'Indent' => 4,
  108. 'Columns' => [ 'Name', 'Description' ])
  109. framework.auxiliary.each_module { |name, mod|
  110. tbl << [ 'auxiliary/' + name, mod.new.name ]
  111. }
  112. ext << tbl.to_s + "\n"
  113. ext
  114. end
  115. #
  116. # Payload naming style is kind of inconsistent, so instead of
  117. # finding the exact path name, we provide the most educated guess (whitelist)
  118. # based on platform/stage type/session type/payload name suffix/etc.
  119. #
  120. def guess_payload_name(p)
  121. matches = []
  122. payload = p.split('/')
  123. platform = payload[0]
  124. suffix = payload[-1]
  125. stage_types = ['singles', 'stagers', 'stages']
  126. session_types = ['meterpreter', 'shell']
  127. arch = ''
  128. # Rule out some possibilities
  129. if p =~ /meterpreter/i
  130. session_types.delete('shell')
  131. stage_types.delete('singles')
  132. end
  133. if p =~ /shell\/.+$/i
  134. session_types.delete('meterpreter')
  135. stage_types.delete('singles')
  136. end
  137. if p =~ /x64/i
  138. arch = 'x64'
  139. elsif p =~ /x86/i
  140. arch = 'x86'
  141. end
  142. # Determine if the payload is staged. If it is, then
  143. # we need to load that staged module too.
  144. if session_types.include?('shell') and stage_types.include?('stages')
  145. if arch == 'x64'
  146. matches << /stages\/#{platform}\/x64\/shell/
  147. elsif arch == 'x86'
  148. matches << /stages\/#{platform}\/x86\/shell/
  149. else
  150. matches << /stages\/#{platform}\/shell/
  151. end
  152. elsif session_types.include?('meterpreter') and stage_types.include?('stages')
  153. if arch == 'x64'
  154. matches << /stages\/#{platform}\/x64\/meterpreter/
  155. elsif arch == 'x86'
  156. matches << /stages\/#{platform}\/x86\/meterpreter/
  157. else
  158. matches << /stages\/#{platform}\/meterpreter/
  159. end
  160. end
  161. # Guess the second possible match
  162. stage_types *= "|"
  163. session_types *= "|"
  164. if arch == 'x64'
  165. matches << /payloads\/(#{stage_types})\/#{platform}\/x64\/.*(#{suffix})\.rb$/
  166. elsif arch == 'x86'
  167. matches << /payloads\/(#{stage_types})\/#{platform}\/x86\/.*(#{suffix})\.rb$/
  168. else
  169. matches << /payloads\/(#{stage_types})\/#{platform}\/.*(#{suffix})\.rb$/
  170. end
  171. matches
  172. end
  173. #
  174. # Returns a whitelist for encoder modules
  175. #
  176. def guess_encoder_name(e)
  177. [/encoders\/#{e}/]
  178. end
  179. #
  180. # Returns a whitelist for nop modules
  181. #
  182. def guess_nop_name(n)
  183. [/nops\/#{n}/]
  184. end
  185. #
  186. # Returns a whitelist for post modules
  187. #
  188. def guess_post_name(p)
  189. [/post\/#{p}/]
  190. end
  191. #
  192. # Returns possible patterns like exploit/aux, encoders, nops we want to
  193. # load to the whitelist.
  194. #
  195. def generate_whitelist
  196. whitelist = []
  197. whitelist << /#{@args[:module_name]}/ # Add exploit
  198. # nil = not set, empty = manually set to load nothing
  199. encoder_val = nil
  200. nops_val = nil
  201. post_val = nil
  202. payload_param = ''
  203. junk_args = []
  204. @args[:params].each { |args|
  205. var, val = args.split('=', 2)
  206. next if val.nil?
  207. case var.downcase
  208. when 'payload'
  209. payload_param = val
  210. if val.empty?
  211. junk_args << args
  212. else
  213. whitelist.concat(guess_payload_name(val))
  214. end
  215. when 'encoder'
  216. encoder_val = val
  217. if val.empty?
  218. junk_args << args
  219. else
  220. whitelist.concat(guess_encoder_name(val))
  221. end
  222. when 'nop'
  223. nops_val = val
  224. if val.empty?
  225. junk_args << args
  226. else
  227. whitelist.concat(guess_nop_name(val))
  228. end
  229. when 'post'
  230. post_val = val
  231. if val.empty?
  232. junk_args << args
  233. else
  234. whitelist.concat(guess_post_name(val))
  235. end
  236. end
  237. }
  238. # Cleanup empty args
  239. junk_args.each { |args| @args[:params].delete(args) }
  240. # If it's an exploit and no payload set, load them all.
  241. if @args[:module_name] !~ /auxiliary\// and payload_param.empty?
  242. whitelist << /payloads\/.+/
  243. end
  244. # Add post modules list if not set
  245. if post_val.nil?
  246. whitelist << /post\/.+/
  247. end
  248. # Add default encoders if not set
  249. # This one is needed no matter what
  250. whitelist << /encoders\/generic\/*/
  251. if encoder_val.nil?
  252. if payload_param =~ /^.+\.x64.+/
  253. whitelist << /encoders\/x64\/.+/
  254. elsif payload_param =~ /^.+\.x86.+/
  255. whitelist << /encoders\/x86\/.+/
  256. else
  257. whitelist << /encoders\/.+/
  258. end
  259. end
  260. # Add default NOP modules if not set
  261. if nops_val.nil?
  262. whitelist << /nops\/.+/
  263. end
  264. whitelist
  265. end
  266. #
  267. # Initializes exploit/payload/encoder/nop modules.
  268. #
  269. def init_modules
  270. $stdout.puts "[*] Initializing modules..."
  271. module_name = @args[:module_name]
  272. modules = {
  273. :module => nil, # aux or exploit instance
  274. :payload => nil, # payload instance
  275. :encoder => nil, # encoder instance
  276. :nop => nil # nop instance
  277. }
  278. whitelist = generate_whitelist
  279. # Load up all the possible modules, this is where things get slow again
  280. framework.init_module_paths({:whitelist=>whitelist})
  281. if (framework.modules.module_load_error_by_path.length > 0)
  282. print("Warning: The following modules could not be loaded!\n\n")
  283. framework.modules.module_load_error_by_path.each do |path, error|
  284. print("\t#{path}: #{error}\n\n")
  285. end
  286. return {}
  287. end
  288. # Determine what type of module it is
  289. if module_name =~ /exploit\/(.*)/
  290. modules[:module] = framework.exploits.create($1)
  291. elsif module_name =~ /auxiliary\/(.*)/
  292. modules[:module] = framework.auxiliary.create($1)
  293. elsif module_name =~ /post\/(.*)/
  294. modules[:module] = framework.post.create($1)
  295. else
  296. modules[:module] = framework.exploits.create(module_name)
  297. if modules[:module].nil?
  298. # Try falling back on aux modules
  299. modules[:module] = framework.auxiliary.create(module_name)
  300. end
  301. end
  302. if modules[:module].nil?
  303. # Still nil? Ok then, probably invalid
  304. return {}
  305. end
  306. modules[:module].init_ui(
  307. Rex::Ui::Text::Input::Stdio.new,
  308. Rex::Ui::Text::Output::Stdio.new
  309. )
  310. # Import options
  311. begin
  312. modules[:module].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_')
  313. rescue Rex::ArgumentParseError => e
  314. raise e
  315. end
  316. # Create the payload to use
  317. if (modules[:module].datastore['PAYLOAD'])
  318. modules[:payload] = framework.payloads.create(modules[:module].datastore['PAYLOAD'])
  319. if modules[:payload]
  320. modules[:payload].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_')
  321. end
  322. end
  323. # Create the encoder to use
  324. if modules[:module].datastore['ENCODER']
  325. modules[:encoder] = framework.encoders.create(modules[:module].datastore['ENCODER'])
  326. if modules[:encoder]
  327. modules[:encoder].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_')
  328. end
  329. end
  330. # Create the NOP to use
  331. if modules[:module].datastore['NOP']
  332. modules[:nop] = framework.nops.create(modules[:module].datastore['NOP'])
  333. if modules[:nop]
  334. modules[:nop].datastore.import_options_from_s(@args[:params].join('_|_'), '_|_')
  335. end
  336. end
  337. modules
  338. end
  339. def show_summary(m)
  340. readable = Msf::Serializer::ReadableText
  341. $stdout.puts("\n" + readable.dump_module(m[:module], @indent))
  342. $stdout.puts("\n" + readable.dump_module(m[:payload], @indent)) if m[:payload]
  343. $stdout.puts("\n" + readable.dump_module(m[:encoder], @indent)) if m[:encoder]
  344. $stdout.puts("\n" + readable.dump_module(m[:nop], @indent)) if m[:nop]
  345. end
  346. def show_options(m)
  347. readable = Msf::Serializer::ReadableText
  348. $stdout.puts("\n" + readable.dump_options(m[:module], @indent))
  349. $stdout.puts("\nPayload:\n\n" + readable.dump_options(m[:payload], @indent)) if m[:payload]
  350. $stdout.puts("\nEncoder:\n\n" + readable.dump_options(m[:encoder], @indent)) if m[:encoder]
  351. $stdout.puts("\nNOP\n\n" + readable.dump_options(m[:nop], @indent)) if m[:nop]
  352. end
  353. def show_missing(m)
  354. readable = Msf::Serializer::ReadableText
  355. $stdout.puts("\n" + readable.dump_options(m[:module], @indent, true))
  356. $stdout.puts("\nPayload:\n\n" + readable.dump_options(m[:payload], @indent, true)) if m[:payload]
  357. $stdout.puts("\nEncoder:\n\n" + readable.dump_options(m[:encoder], @indent, true)) if m[:encoder]
  358. $stdout.puts("\nNOP\n\n" + readable.dump_options(m[:nop], @indent, true)) if m[:nop]
  359. end
  360. def show_advanced(m)
  361. readable = Msf::Serializer::ReadableText
  362. $stdout.puts("\n" + readable.dump_advanced_options(m[:module], @indent))
  363. $stdout.puts("\nPayload:\n\n" + readable.dump_advanced_options(m[:payload], @indent)) if m[:payload]
  364. $stdout.puts("\nEncoder:\n\n" + readable.dump_advanced_options(m[:encoder], @indent)) if m[:encoder]
  365. $stdout.puts("\nNOP:\n\n" + readable.dump_advanced_options(m[:nop], @indent)) if m[:nop]
  366. end
  367. def show_ids_evasion(m)
  368. readable = Msf::Serializer::ReadableText
  369. $stdout.puts("\n" + readable.dump_evasion_options(m[:module], @indent))
  370. $stdout.puts("\nPayload:\n\n" + readable.dump_evasion_options(m[:payload], @indent)) if m[:payload]
  371. $stdout.puts("\nEncoder:\n\n" + readable.dump_evasion_options(m[:encoder], @indent)) if m[:encoder]
  372. $stdout.puts("\nNOP:\n\n" + readable.dump_evasion_options(m[:nop], @indent)) if m[:nop]
  373. end
  374. def show_payloads(m)
  375. readable = Msf::Serializer::ReadableText
  376. txt = "Compatible payloads"
  377. $stdout.puts("\n" + readable.dump_compatible_payloads(m[:module], @indent, txt))
  378. end
  379. def show_targets(m)
  380. readable = Msf::Serializer::ReadableText
  381. $stdout.puts("\n" + readable.dump_exploit_targets(m[:module], @indent))
  382. end
  383. def show_actions(m)
  384. readable = Msf::Serializer::ReadableText
  385. $stdout.puts("\n" + readable.dump_module_actions(m[:module], @indent))
  386. end
  387. def show_check(m)
  388. begin
  389. if (code = m[:module].check_simple(
  390. 'LocalInput' => Rex::Ui::Text::Input::Stdio.new,
  391. 'LocalOutput' => Rex::Ui::Text::Output::Stdio.new))
  392. stat = (code == Msf::Exploit::CheckCode::Vulnerable) ? '[+]' : '[*]'
  393. $stdout.puts("#{stat} #{code[1]}")
  394. else
  395. $stdout.puts("Check failed: The state could not be determined.")
  396. end
  397. rescue
  398. $stdout.puts("Check failed: #{$!}")
  399. end
  400. end
  401. def execute_module(m)
  402. con = Msf::Ui::Console::Driver.new(
  403. Msf::Ui::Console::Driver::DefaultPrompt,
  404. Msf::Ui::Console::Driver::DefaultPromptChar,
  405. {
  406. 'Framework' => framework,
  407. # When I use msfcli, chances are I want speed, so ASCII art fanciness
  408. # probably isn't much of a big deal for me.
  409. 'DisableBanner' => true
  410. })
  411. module_class = (m[:module].fullname =~ /^auxiliary/ ? 'auxiliary' : 'exploit')
  412. con.run_single("use #{module_class}/#{m[:module].refname}")
  413. # Assign console parameters
  414. @args[:params].each do |arg|
  415. k,v = arg.split("=", 2)
  416. con.run_single("set #{k} #{v}")
  417. end
  418. # Run the exploit
  419. con.run_single("exploit")
  420. # If we have sessions or jobs, keep running
  421. if framework.sessions.length > 0 or framework.jobs.length > 0
  422. con.run
  423. else
  424. con.run_single("quit")
  425. end
  426. end
  427. #
  428. # Selects a mode chosen by the user and run it
  429. #
  430. def engage_mode(modules)
  431. case @args[:mode].downcase
  432. when 'h'
  433. usage
  434. when "s"
  435. show_summary(modules)
  436. when "o"
  437. show_options(modules)
  438. when "m"
  439. show_missing(modules)
  440. when "a"
  441. show_advanced(modules)
  442. when "i"
  443. show_ids_evasion(modules)
  444. when "p"
  445. if modules[:module].file_path =~ /auxiliary\//i
  446. $stdout.puts("\nError: This type of module does not support payloads")
  447. else
  448. show_payloads(modules)
  449. end
  450. when "t"
  451. puts
  452. if modules[:module].file_path =~ /auxiliary\//i
  453. $stdout.puts("\nError: This type of module does not support targets")
  454. else
  455. show_targets(modules)
  456. end
  457. when "ac"
  458. if modules[:module].kind_of?(Msf::Module::HasActions)
  459. show_actions(modules)
  460. else
  461. $stdout.puts("\nError: This type of module does not support actions")
  462. end
  463. when "c"
  464. show_check(modules)
  465. when "e"
  466. execute_module(modules)
  467. else
  468. usage("Invalid mode #{@args[:mode]}")
  469. end
  470. end
  471. def run!
  472. if @args[:module_name] == "-h"
  473. usage()
  474. exit
  475. end
  476. $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
  477. require 'msfenv'
  478. require 'msf/ui'
  479. require 'msf/base'
  480. if @args[:module_name].nil?
  481. ext = dump_module_list
  482. usage(nil, ext)
  483. exit
  484. end
  485. begin
  486. modules = init_modules
  487. rescue Rex::ArgumentParseError => e
  488. puts "[!] Error: #{e.message}\n\n"
  489. exit
  490. end
  491. if modules[:module].nil?
  492. usage("Invalid module: #{@args[:module_name]}")
  493. exit
  494. end
  495. # Process special var/val pairs...
  496. Msf::Ui::Common.process_cli_arguments(framework, @args[:params])
  497. engage_mode(modules)
  498. $stdout.puts
  499. end
  500. end
  501. if __FILE__ == $PROGRAM_NAME
  502. cli = Msfcli.new(ARGV)
  503. cli.run!
  504. end