# 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, } 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)) 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]) s[:proto]="ftp" s[:extra]="Failed Login. Banner: #{s[:banner]}" report_auth_info(s) print_status("Failed FTP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") s[:pass]="" return end when :login_pass if(s[:user] and s[:pass]) s[:proto]="ftp" s[:extra]="Successful Login. Banner: #{s[:banner]}" report_auth_info(s) print_status("Successful FTP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") # 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[:banner]) sessions[s[:session]].merge!({k => matches}) s[:name]="FTP Server Welcome Banner: \"#{s[:banner]}\"" report_service(s) end 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