-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdetect-hacker.py
executable file
·136 lines (112 loc) · 5.21 KB
/
detect-hacker.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#!/usr/bin/python
# Usage detect-hacker.py --logfile /var/log/messages --duration 60 --threshold 6
from optparse import OptionParser
import sys
import re
import time
import fpformat
import os
debug = False
# change this to match where your iptables is
iptables = "/usr/sbin/iptables"
# parse the command line
parser = OptionParser()
parser.add_option("-l", "--logfile", dest="logfile", help="logfile (required)")
parser.add_option("-d", "--duration", dest="duration", type="int", help="Number of minutes back to look for failed logins (default 60)")
parser.add_option("-t", "--threshold", dest="threshold", type="int", help="Number of failed logins to be banned (default 6)")
(options, args) = parser.parse_args()
if options.logfile == None:
print "A logfile must be specified"
parser.print_help()
sys.exit()
duration = 60
if not options.duration == None:
duration = options.duration
threshold = 6
if not options.threshold == None:
thresold = options.threshold
# keep track of user accounts that count for failed logins
system_accounts = []
user_accounts = []
passwd_file = open('/etc/passwd')
line = passwd_file.readline()
while not line == '':
line_match = re.search('^(?P<user>[^:]+):[^:]*:[^:]*:[^:]*:[^:]*:(?P<home>[^:]*)', line)
if line_match:
if not re.search('/home', line_match.group('home')):
system_accounts.append(line_match.group('user'))
else:
user_accounts.append(line_match.group('user'))
line = passwd_file.readline()
passwd_file.close()
# keep track of failed logins
failed_logins = {}
# calculate date to check after -> now() - (duration + duration/2) minutes (overlap to catch corner cases)
#checkdate = time.time() - (duration * 60 + (duration * 60 / 2))
checkdate = time.time() - (duration * 60)
if debug:
print "checkdate: " + time.strftime('%m/%d/%Y %H:%M:%S', time.gmtime(checkdate)) + " -> " + fpformat.fix(checkdate, 0)
# open logfile
logfile = open(options.logfile)
done = False
while not done:
line = logfile.readline()
if line == '':
# hit EOF
done = True
else:
#line_match = re.search('(?P<date>[A-Z][a-z][a-z]\s{1,2}\d{1,2}\s\d{2}:\d{2}:\d{2}).*sshd\[\d+\].*(?:(?P<invalid1>Invalid|Illegal user)|(:?Failed password for (?:(?P<invalid2>invalid|illegal) user )?))(?P<user>\S+) from (?:::ffff:)?(?P<ip>\S+)', line)
line_match = re.search('(?P<date>[A-Z][a-z][a-z]\s{1,2}\d{1,2}\s\d{2}:\d{2}:\d{2}).*sshd\[\d+\].*(?:((?:Invalid|Illegal) user )|(:?Failed password for (?:(invalid|illegal) user )?)|(error: PAM: Authentication failure for ))(?P<user>\S+) from (?:::ffff:)?(?P<ip>\S+)', line)
if line_match:
# look forward until find a log entry after checkdate
year = time.localtime().tm_year
date_str = str(year) + " " + line_match.group('date')
logentry_date_str = time.strptime(date_str, '%Y %b %d %H:%M:%S');
logentry_date = time.mktime( (year, logentry_date_str.tm_mon, logentry_date_str.tm_mday, logentry_date_str.tm_hour, logentry_date_str.tm_min, logentry_date_str.tm_sec, logentry_date_str.tm_wday, -1, -1) )
# adjust for end of year
if(time.localtime().tm_mon < time.localtime(logentry_date).tm_mon):
year = year - 1
logentry_date = time.mktime( (year, logentry_date_str.tm_mon, logentry_date_str.tm_mday, logentry_date_str.tm_hour, logentry_date_str.tm_min, logentry_date_str.tm_sec, logentry_date_str.tm_wday, -1, -1) )
if logentry_date >= checkdate:
if debug:
print "logentry_date: " + time.strftime('%m/%d/%Y %H:%M:%S', time.gmtime(logentry_date)) + " -> " + fpformat.fix(logentry_date, 0)
# check if invalid or a system account
#if line_match.group('invalid1') or line_match.group('invalid2') or line_match.group('user') in system_accounts:
if not line_match.group('user') in user_accounts:
# increment counter for ip
ip = line_match.group('ip')
if debug:
print "Incrementing counter", ip, "from", line
if failed_logins.has_key(ip):
failed_logins[ip] = failed_logins[ip] + 1
else:
failed_logins[ip] = 1
#else:
# print "Not invalid? invalid:", line_match.group('invalid1'), line_match.group("invalid2"), line
#elif re.search('.*sshd', line):
# print "Line doesn't match:", line
logfile.close()
# read in existing hackers file & delete those IPs from the list of new
# hackers
hackers = open('/etc/hackers', 'r')
for line in hackers:
line_match = re.match(r'# Automatically blocked (?P<ip>[\d.:A-F]+) on', line)
if line_match:
if failed_logins.has_key(line_match.group('ip')):
if debug:
print("Removing " + line_match.group('ip'))
del failed_logins[line_match.group('ip')]
hackers.close()
# walk over counters and any counter over threshold gets banned
for ip, count in failed_logins.iteritems():
if count > threshold:
time_str = time.strftime('%m/%d/%Y %H:%M:%S')
msg = "# Automatically blocked " + ip + " on " + time_str + " for " + fpformat.fix(count, 0) + " failed login attempts"
if not debug:
hackers = open('/etc/hackers', 'a')
hackers.write(msg + "\n");
hackers.write(ip + "\n");
hackers.close()
print msg
if not debug:
os.system(iptables + " -I INPUT -s " + ip + " -j DROP")