-
Notifications
You must be signed in to change notification settings - Fork 2
/
_Check_Logins
263 lines (227 loc) · 9.77 KB
/
_Check_Logins
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
#!/usr/bin/perl
#
# Author: Jim Dunphy <jad aesir.com>
# License (ISC): It's yours. Enjoy
# Date: 10/9/2015
# Update on feedback from Lapsy from forums.zimbra.com 10/13/2018
# Update on Thiago Castro 07/06/2023
#
# usage: _Check_Logins [Options]
#
# ======================================================================
print "\033[2J"; #clear the screen
print "\033[0;0H"; #jump to 0,0
#========================================================================
# SECTION - Modules, Variables, etc.
#========================================================================
use Term::ANSIColor;
use Data::Dumper qw(Dumper);
use Getopt::Long;
use Socket qw( inet_aton AF_INET );
%ip_list = (); #ip list
%fip_list = (); #failed ip list
#========================================================================
# SECTION - FUNCTIONS
#========================================================================
# Displays program usage
sub usage {
print <<"END";
usage: % _Check_Logins
[--color=<color name (i.e. RED)>]
[--srchuser=<username>]
[--fail=<user|ip|none>]
[--gethost=<all|fail|none>]
[--help]
where:
--color|c: #color to be used for FAILED login message information
--srchuser|s: #print ONLY the logins/failed logins for <username>
--fail|f: if 'user': #print ONLY users who have failed logins. If 'ip': print ONLY the failed login attempts. 'none': print all records regardless if failure
--gethost|g: values of 'all', 'fail' or 'none' #Perform a GETHOSTBYADDR for all IPs, only on FAILED login attempts, or don't perform this action (none)
--help|h: this message
example:
% _Check_Logins -f=user #only the accounts with failed logins
% _Check_Logins -f ip #only the accounts and the ip that failed
% _Check_Logins -fail=ip # same as above
% _Check_Logins --fail ip # same as above
% _Check_Logins -f ip -h #list only ip's that failed for accounts resolve ip to domain name
% _Check_Logins -g fail #list only accounts that had a failed login
% _Check_Logins -g all #list all accounts and resolve ip to domain name
% _Check_Logins -c RED -f ip #change color and list only failed ip's
% _Check_Logins -s domain -f ip -g fail #list all failed ip addresses with ip to domain name
% _Check_Logins -s email -f ip -g fail #list all failed ip addresses with ip to e-mail name
END
exit 0;
}
sub setlists {
my ($user, $ip, $typeval) = @_;
++$ip_list{$user}{$ip}; #we loop by this for report
++$fip_list{$user}{$ip}{'count'};
++$fip_list{$user}{$ip}{$typeval};
return;
}
# get the hostname for the iP address if requested
sub gethostname {
my ($gethost) = @_;
my $attacker = "";
if (($gethost =~ m/all/i)
|| (($gethost =~ /fail/i) && ((exists $fip_list{$user}{$ip}) && ($fip_list{$user}{$ip}{count})))) {
if((gethostbyaddr(inet_aton($ip), AF_INET)) =~ /([a-z0-9_\-]{1,5})?(:\/\/)?(([a-z0-9_\-]{1,})(:([a-z0-9_\-]{1,}))?\@)?((www\.)|([a-z0-9_\-]{1,}\.)+)?([a-z0-9_\-]{3,})(\.[a-z]{2,4})(\/([a-z0-9_\-]{1,}\/)+)?([a-z0-9_\-]{1,})?(\.[a-z]{2,})?(\?)?(((\&)?[a-z0-9_\-]{1,}(\=[a-z0-9_\-]{1,})?)+)?/) {
$attacker="$10$11$15";
}
}
return $attacker;
}
# Print results out
sub printresults {
my($ucolor, $uattr, $user, $msgcolor, $msg) = @_;
print color($ucolor, $uattr), " ";
printf "%-47s", $user;
print color('reset');
print color($msgcolor), $msg;
print color('reset');
return;
}
# Pretty clear - output a big line
sub drawline {
print "\n------------------------------------------------------------------------------------------------------------\n";
return;
}
#========================================================================
# SECTION - GET input parameters
#========================================================================
# Get the command line parameters for processing
my $fcolor = 'YELLOW';
my $srchuser = '@';
my $failtype = 'none'; #default failure behavior (user|ip|none)
my $gethost = 'fail'; #default lookup behavior (all|fail|none)
my $help, $dbug = 0;
&GetOptions( "color=s" => \$fcolor,
"srchuser=s" => \$srchuser,
"fail=s" => \$failtype, # user, ip, none
"gethost=s" => \$gethost, # all, fail, none
"debug" => \$dbug, # turn on debugging
"help" => \$help);
# call Help if parameters do not meet expected values or help is requested
usage() if($help || ($failtype !~ m/^user|ip|none$/i) || ($gethost !~ m/^all|fail|none$/i));
#========================================================================
# SECTION - PARSE audit.log files & process accordingly
#========================================================================
chdir "/opt/zimbra/log";
#for (glob 'mailbox.log*') {
for (glob 'audit.log*') {
$lines = 0;
$audit_log = $_ eq 'audit.log' ? 1 : 0;
#print "Opening file $_";
open (IN, sprintf("zcat -f %s |", $_))
or die("Can't open pipe from command 'zcat -f $filename' : $!\n");
# part the audit logs looking for access types
# we just doing this
# zcat -f /opt/zimbra/log/audit.log* | grep -i invalid |egrep '(ImapS|Pop|http)'
while (<IN>)
{
if (m#invalid#i)
{
#print $_;
if ((m#ImapS#i) && !(m#INFO#))
{
my($ip,$user) = m#[oip][ip]=(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3});.* failed for\s+\[(.*)\].*$#i;
# my($ip,$user) = m#.*\s+\[ip=.*;[oip][ip]=(.*);via=.*;\]\s*.* failed for\s+\[(.*)\].*$#i;
$uagent = "imap";
#print " - ip is $ip, user is $user, agent is $uagent\n";
#print $_;
setlists($user, $ip, $uagent);
}
elsif ((m#Pop#i) && !(m#INFO#))
{
my($ip,$user) = m#[oip][ip]=(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3});.* failed for\s+\[(.*)\].*$#i;
$uagent = "pop";
#print " - ip is $ip, user is $user, agent is $uagent\n";
#print $_;
setlists($user, $ip, $uagent);
}
elsif ((m#http#i) && (m#zclient#))
{
my($user,$ip,$uagent) = m#.*\s+\[name=(.*);[oip][ip]=(.*);ua=(.*);\].*$#i;
$uagent = "web";
#print " - ip is $ip, user is $user, agent is $uagent\n";
#print $_;
setlists($user, $ip, $uagent);
}
# elsif ((m#oproto=smtp#i) && (m#failed#))
elsif ((m#smtp#i) && (m#failed#))
{
my($ip,$user) = m#[oip][ip]=(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3});.* failed for\s+\[(.*)\].*$#i;
$uagent = "smtp";
#print " - ip is $ip, user is $user, agent is $uagent\n";
#print $_;
setlists($user, $ip, $uagent);
}
}
elsif (m#AuthRequest#i && ($_ !~ m/zimbra/i))
{
my($user,$ip,$uagent) = m#.*\s+\[name=(.*);[oip][ip]=(.*);ua=(.*);\].*$#i;
++$ip_list{$user}{$ip};
#if ($audit_log == 1) { print $_; }
#printf "%4d: - ip is %15s, user is %45s, agent is %s\n",$lines,$ip,$user,$uagent;
}
$lines++;
} # End While (<IN>) loop
close (IN);
}
#========================================================================
# SECTION - PRINT / MAIN
#========================================================================
#debug
#print Dumper \%ip_list;
#print Dumper \%fip_list;
drawline();
# Print out the arrays by username. Flag failures.
for $user (sort keys %ip_list )
{
# Skip this user, if -s parameter is given and user is not in search string
next if(index($user,$srchuser) == -1);
# Proceed only if we're only looking for users who have failed logins recorded
next if(($failtype =~ /user|ip/i) && !(exists $fip_list{$user}));
$total = 0;
$totalf = 0;
for $ip (sort {$ip_list{$user}{$b} <=> $ip_list{$user}{$a}} keys %{$ip_list{$user}} )
{
# See count of how many times
if(($failtype !~ /ip/i) || (($failtype =~ /ip/) && exists $fip_list{$user}{$ip})) {
printf ("[%4d] Logins do IP %15s ", $ip_list{$user}{$ip},$ip);
}
$total = $total+$ip_list{$user}{$ip}; # Count all for this username
# lookup the domain if requested
my $attacker = "[" . gethostname($gethost) . "]";
if ((exists $fip_list{$user}{$ip}) && ($fip_list{$user}{$ip}{count}))
{
print color($fcolor);
printf "%-30s", $attacker if ($gethost !~ /none/i);
printf " Falhas [%4d] : ", $fip_list{$user}{$ip}{count};
for $etypes (keys %{$fip_list{$user}{$ip}}) {
next if $etypes =~ /count/;
printf " Usando %s [%4d] ", $etypes, $fip_list{$user}{$ip}{$etypes};
}
print color('reset');
printf ("\n");
$totalf = $totalf+$fip_list{$user}{$ip}{count}; # Count all failed for this username
} elsif (($gethost =~ /all/i) && !($failtype =~ /ip/i))
{
printf "$attacker\n";
} elsif ($failtype !~ /ip/) {
printf ("\n");
}
}
# Print out user information & message totals
if ($totalf>0) {
my $msg = sprintf("%d falhas do total de tentativas de login!!!", $totalf, $total);
$msgcolor = $totalf==$total ? "RED" : "YELLOW";
printresults("WHITE", "BOLD", $user, $msgcolor, $msg);
} elsif ($failtype !~ /ip/i) {
my $msg = " Sem logins com falha. Yeeee :)";
printresults("WHITE", "BOLD", $user, "GREEN", $msg);
}
drawline();
}
printf ("\n");
print color('reset'); # make sure we clean up