CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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