2009-08-19 14:07:33 +00:00
|
|
|
# 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
|
2013-09-30 18:47:53 +00:00
|
|
|
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
|
2009-08-19 14:07:33 +00:00
|
|
|
|
2013-09-30 18:47:53 +00:00
|
|
|
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))
|
2009-08-19 14:07:33 +00:00
|
|
|
|
2013-09-30 18:47:53 +00:00
|
|
|
self.sigs.each_key do |k|
|
|
|
|
# There is only one pattern per run to test
|
|
|
|
matched = nil
|
|
|
|
matches = nil
|
2009-08-19 14:07:33 +00:00
|
|
|
|
2013-09-30 18:47:53 +00:00
|
|
|
if(pkt.payload =~ self.sigs[k])
|
|
|
|
matched = k
|
|
|
|
matches = $1
|
|
|
|
end
|
2009-08-19 14:07:33 +00:00
|
|
|
|
2013-09-30 18:47:53 +00:00
|
|
|
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[:info] = matches
|
|
|
|
s[:proto] = "tcp"
|
|
|
|
s[:name] = "pop3"
|
|
|
|
report_service(s)
|
2009-08-19 14:07:33 +00:00
|
|
|
|
2013-09-30 18:47:53 +00:00
|
|
|
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 :-)
|
2009-11-02 17:59:45 +00:00
|
|
|
|
2013-09-30 18:47:53 +00:00
|
|
|
s[:proto] = "tcp"
|
|
|
|
s[:name] = "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})")
|
2009-11-02 17:59:45 +00:00
|
|
|
|
2013-09-30 18:47:53 +00:00
|
|
|
# Remove it form the session objects so freeup
|
|
|
|
sessions.delete(s[:session])
|
2009-11-02 17:59:45 +00:00
|
|
|
|
2013-09-30 18:47:53 +00:00
|
|
|
when :quit
|
|
|
|
# The session is terminated by the user just delete is as well
|
|
|
|
sessions.delete(s[:session])
|
|
|
|
end
|
|
|
|
s[:last]=:ok
|
2009-08-19 14:07:33 +00:00
|
|
|
|
2013-09-30 18:47:53 +00:00
|
|
|
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 :-)
|
2009-11-02 17:59:45 +00:00
|
|
|
|
2013-09-30 18:47:53 +00:00
|
|
|
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
|
2009-08-19 14:07:33 +00:00
|
|
|
end
|
2009-11-02 17:59:45 +00:00
|
|
|
|