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.

pcap_log.rb 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. ##
  2. # $Id$
  3. ##
  4. ##
  5. # This file is part of the Metasploit Framework and may be subject to
  6. # redistribution and commercial restrictions. Please see the Metasploit
  7. # Framework web site for more information on licensing and terms of use.
  8. # https://metasploit.com/framework/
  9. ##
  10. # $Revision$
  11. module Msf
  12. class Plugin::PcapLog < Msf::Plugin
  13. # Only little-endian is supported in this implementation.
  14. PCAP_FILE_HEADER = "\xD4\xC3\xB2\xA1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x01\x00\x00\x00"
  15. #
  16. # Implements a pcap console command dispatcher.
  17. #
  18. class PcapLogDispatcher
  19. include Msf::Ui::Console::CommandDispatcher
  20. def name
  21. "PcapLog"
  22. end
  23. def commands
  24. {
  25. "pcap_filter" => "Set/Get a BPF-style packet filter",
  26. "pcap_dir" => "Set/Get a directory to log pcaps to",
  27. "pcap_prefix" => "Set/Get a filename prefix to log pcaps to",
  28. "pcap_iface" => "Set/Get an interface to capture from",
  29. "pcap_start" => "Start a capture",
  30. "pcap_stop" => "Stop a running capture",
  31. "pcap_show_config" => "Show the current PcapLog configuration"
  32. }
  33. end
  34. def cmd_pcap_filter(*args)
  35. @filter = args.join(' ') || @filter
  36. print_line "#{self.name} BPF filter: #{@filter}"
  37. end
  38. def cmd_pcap_prefix(*args)
  39. @prefix = args[0] || @prefix || "msf3-session"
  40. print_line "#{self.name} prefix: #{@prefix}"
  41. end
  42. def cmd_pcap_dir(*args)
  43. @dir = args[0] || @dir || "/tmp"
  44. print_line "#{self.name} Directory: #{@dir}"
  45. end
  46. def cmd_pcap_iface(*args)
  47. @iface = args[0] || @iface
  48. print_line "#{self.name} Interface: #{@iface}"
  49. end
  50. def cmd_pcap_start(*args)
  51. unless @pcaprub_loaded
  52. print_error("Pcap module not available")
  53. return false
  54. end
  55. if @capture_thread && @capture_thread.alive?
  56. print_error "Capture already started."
  57. return false
  58. end
  59. gen_fname
  60. print_line "Starting packet capture from #{@iface} to #{@fname}"
  61. okay,msg = validate_options
  62. unless okay
  63. print_error msg
  64. return false
  65. end
  66. dev = (@iface || ::Pcap.lookupdev)
  67. @capture_file.write(PCAP_FILE_HEADER)
  68. @capture_file.flush
  69. @pcap = ::Pcap.open_live(dev, 65535, true, 1)
  70. @pcap.setfilter(@filter) if @filter
  71. @capture_thread = Thread.new {
  72. @pcap.each do |pkt|
  73. @capture_file.write(convert_to_pcap(pkt))
  74. @capture_file.flush
  75. end
  76. }
  77. end
  78. def cmd_pcap_stop(*args)
  79. if @capture_thread && @capture_thread.alive?
  80. print_line "Stopping packet capture from #{@iface} to #{@fname}"
  81. print_line "Capture Stats: #{@pcap.stats.inspect}"
  82. @pcap = nil
  83. @capture_file.close if @capture_file.respond_to? :close
  84. @capture_thread.kill
  85. @capture_thread = nil
  86. else
  87. print_error "No capture running."
  88. end
  89. end
  90. def convert_to_pcap(packet)
  91. t = Time.now
  92. sz = packet.size
  93. [t.to_i, t.usec, sz, sz, packet].pack("V4A*")
  94. end
  95. def gen_fname
  96. t = Time.now
  97. file_part = "%s_%04d-%02d-%02d_%02d-%02d-%02d.pcap" % [
  98. @prefix, t.year, t.month, t.mday, t.hour, t.min, t.sec
  99. ]
  100. @fname = File.join(@dir, file_part)
  101. end
  102. # Check for euid 0 and check for a valid place to write files
  103. def validate_options
  104. # Check for root.
  105. unless Process.euid.zero?
  106. msg = "You must run as root in order to capture packets."
  107. return [false, msg]
  108. end
  109. # Check directory suitability.
  110. unless File.directory? @dir
  111. msg = "Invalid pcap directory specified: '#{@dir}'"
  112. return [false, msg]
  113. end
  114. unless File.writable? @dir
  115. msg = "No write permission to directory: '#{@dir}'"
  116. return [false, msg]
  117. end
  118. @capture_file = File.open(@fname, "ab")
  119. unless File.writable? @fname
  120. msg = "Cannot write to file: '#{@fname}'"
  121. return [false, msg]
  122. end
  123. # If you got this far, you're golden.
  124. msg = "We're good!"
  125. return [true, msg]
  126. end
  127. # Need to pretend to have a datastore for Exploit::Capture to
  128. # function.
  129. def datastore
  130. {}
  131. end
  132. def initialize(*args)
  133. super
  134. @dir = File.join(Msf::Config.config_directory, 'logs')
  135. @prefix = "msf3-session"
  136. @filter = nil
  137. @pcaprub_loaded = false
  138. begin
  139. require 'pcaprub'
  140. @pcaprub_loaded = true
  141. @iface = ::Pcap.lookupdev
  142. rescue ::Exception => e
  143. print_error "#{e.class}: #{e}"
  144. @pcaprub_loaded = false
  145. @pcaprub_error = e
  146. end
  147. end
  148. end
  149. def initialize(framework, opts)
  150. super
  151. add_console_dispatcher(PcapLogDispatcher)
  152. print_status "PcapLog plugin loaded."
  153. end
  154. # Kill the background thread
  155. def cleanup
  156. @capture_thread.kill if @capture_thread && @capture_thread.alive?
  157. @capture_file.close if @capture_file.respond_to? :close
  158. remove_console_dispatcher('PcapLog')
  159. end
  160. def name
  161. "pcap_log"
  162. end
  163. def desc
  164. "Logs all socket operations to pcaps (in /tmp by default)"
  165. end
  166. end
  167. end