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