Initial revision
0 parent commit 5803321c9b1da21656e96d640abad3244912b88f
@tundra tundra authored on 16 Jul 2001
Showing 1 changed file
View
231
abck 0 → 100755
#!/usr/local/bin/python
# Build a report of all unauthorized access attempts
 
####################
# Imports
####################
 
import commands
import re
import sys
 
####################
# Booleans
####################
 
FALSE = 0 == 1
TRUE = not FALSE
 
DONE = FALSE
 
####################
# Constants And Literals
####################
 
ANS = ";; ANSWER SECTION:"
AUTH = ";; AUTHORITY SECTION:"
DIG = "/usr/bin/dig -t ptr -x "
LOG = "/var/log/messages"
OUT = "./abusers"
 
VERSION = "$Id: abck,v 1.1 2001/07/16 20:00:50 tundra Exp $"
 
####################
# Data Structures
####################
 
# Dictionary of keywords indicating attack, and position of host address/name
# in their log records
 
AttackKeys = {
"refused" : 8,
"unauthorized" : 7
}
 
# Cache dictionary of all attacking hosts discovered this run of the program
 
NameCache = {}
 
####################
# Regular Expression Handlers
####################
 
# Regular Expression which describes a legit IP quad address
 
IPQuad = r"^((\d{1,3}\.){3}(\d\d?\d?))$"
 
####################
# Function Definitions
####################
 
# Return the ending substring of a host name with 'depth' number of dots
# in it
 
def HostDepth(host, depth=2):
 
# Break the address down into components
components = host.split(".")
 
# And return the recombined pieces we want
return '.'.join(components[-depth:])
 
####################
 
# Check a name, see if it's an IP quad, and if so, return reverse.
# If not, return the original name.
#
# This is better than a socket.gethostbyaddr() call because
# this will return the authority information for an address
# if no explicit reverse resolution is found.
 
def CheckIPReverse(hostquad):
# If it's an IP address in quad form, reverse resolve it
if re.match(IPQuad, hostquad):
 
DIGResults = commands.getoutput(DIG + hostquad).splitlines()
 
ansname = ""
authname = hostquad
# Results must either have an Answer or Authority record
if ( DIGResults.count(ANS) + DIGResults.count(AUTH)):
 
i = 0
while i < len(DIGResults):
 
if DIGResults[i].startswith(ANS):
ansname = DIGResults[i+1].split()[4]
 
if DIGResults[i].startswith(AUTH):
authname = DIGResults[i+1].split()[-2]
 
i += 1
 
if ansname:
hostname = ansname
else:
hostname = authname
 
# Get rid of trailing dot, if any
if hostname[-1] == '.':
hostname = hostname[:-1]
 
else:
hostname = hostquad
return hostname
 
 
####################
 
# Paw through a log record, doing any reverse rosolution needed,
# confirm with user, and return name of the host to notify about
# the instrusion attempt. A null return means the user want to
# skip this record.
 
 
def ProcessLogRecord(logrecord):
 
# Check for each known attack keyword
 
sendto = ""
for attackkey in AttackKeys.keys():
 
logfield = logrecord.split()
if logrecord.count(attackkey):
 
# Different attack records put the hostquad in different places
hostquad = logfield[AttackKeys[attackkey]]
if hostquad[-1] == ',':
hostquad = hostquad[:-1] # Strip trailing dots
 
# Go do a reverse resolution if we need to
hostname = CheckIPReverse(hostquad)
 
# Check for the case of getting a PTR record back
hostname = ReversePTR(hostname)
 
# Check if we've seen this abuser before
if NameCache.has_key(hostname):
sendto = NameCache[hostname]
 
# New one
else:
depth = 2
DONE=FALSE
while not DONE:
 
# Set depth of default response
default = HostDepth(hostname, depth)
 
# Ask the user about it
st = raw_input("Who Gets Message for: <%s>? %s [%s] " %
(hostname[-40:],
" " * (40 - len(hostname)),
default))
 
# Parse the response
if st == "!": # Skip this record
sendto = ""
DONE = TRUE
 
elif st.lower() == "r": # Less depth in recipient name
if depth > 2:
depth -= 1
 
elif st.lower() == "l": # More depth in recipient name
if depth < len(hostname.split('.')):
depth += 1
 
else:
if st: # User keyed in their own recipient
sendto = st
else: # User accepted the default
sendto = default
DONE = TRUE
NameCache[hostname] = sendto # Cache it
return sendto
 
####################
 
# Check the passed hostname and see if it looks like a PTR record.
# If so, strip out the address portion, reverse it, and trying
# doing another reverse lookup. If not, just return the original hostname.
 
def ReversePTR(hostname):
 
tmp = hostname.split("in-addr.arpa")
 
if len(tmp) > 1: # Looks like a PTR record
 
tmp = tmp[0].split('.') # Get addr components
tmp.reverse() # and reverse their order
# Take the 1st four quads (some PTR records have more)
# and see if we can dig out a reverse.
hostname = CheckIPReverse('.'.join(tmp[1:5]))
 
return hostname
 
 
#------------------------- Program Entry And Mail Loop -----------------------#
 
logfile = open(LOG)
abuserfile = open(OUT, "w")
 
# Read in the whole log logfileile
for logrecord in logfile.read().splitlines():
 
# Go check the record in no command line constraint given
# or a constraint is given and exits in the record
if not sys.argv[1] or logrecord.count(sys.argv[1]):
 
sendto = ProcessLogRecord(logrecord)
if sendto:
abuserfile.write("abnot \"" + logrecord + "\" " +
sendto + "\n")
 
logfile.close()
abuserfile.close()