One of the roles of System and Linux Administrator is to monitor user login history so that they are aware of system activity. These are very important from security perspective to track the number of users who connect to any Linux server. You can use the commands from this script to track this successful and failed logins manually or use the script from this tutorial which will give you a summarised and detail output of all Linux login history.
Monitoring user logins to find intruders
Log files can be used to gather details about the state of the system and attacks on the system.
Suppose we have a system connected to the Internet with SSH enabled. Many attackers are trying to log in to the system. We need to design an intrusion detection system to identify users who fail their login attempts. Such attempts may be of a hacker using a dictionary attack. The script should generate a report with the following details:
User with successful attempts and that failed to log in
- Number of attempts
- IP address of the attacker
- Host mapping for the IP address
- Time when login attempts occurred
Log Files to check login attempts
Based on your distribution the log files to check login history will differ. On my RHEL/CentOS 7/8 Linux node these information are captured in /var/log/secure
. But in some distribution this is captured in /var/log/auth.log
Shell script to check Linux Login History
Below is a sample shell script which will check successful and failed login attempts on Linux node using /var/log/secure
.
#!/bin/bash
# Filename: intruder_detect.sh
# Description: Check Linux Login History
AUTHLOG=/var/log/secure
if [[ -n $1 ]];
then
AUTHLOG=$1
echo Using Log file : $AUTHLOG
fi
# Collect the failed login attempts
FAILED_LOG=/tmp/failed.$$.log
egrep "Failed pass" $AUTHLOG > $FAILED_LOG
# Collect the successful login attempts
SUCCESS_LOG=/tmp/success.$$.log
egrep "Accepted password|Accepted publickey|keyboard-interactive" $AUTHLOG > $SUCCESS_LOG
# extract the users who failed
failed_users=$(cat $FAILED_LOG | awk '{ print $(NF-5) }' | sort | uniq)
# extract the users who successfully logged in
success_users=$(cat $SUCCESS_LOG | awk '{ print $(NF-5) }' | sort | uniq)
# extract the IP Addresses of successful and failed login attempts
failed_ip_list="$(egrep -o "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" $FAILED_LOG | sort | uniq)"
success_ip_list="$(egrep -o "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" $SUCCESS_LOG | sort | uniq)"
# Print the heading
printf "%-10s|%-10s|%-10s|%-15s|%-15s|%s\n" "Status" "User" "Attempts" "IP address" "Host" "Time range"
# Loop through IPs and Users who failed.
for ip in $failed_ip_list;
do
for user in $failed_users;
do
# Count failed login attempts by this user from this IP
attempts=`grep $ip $FAILED_LOG | grep " $user " | wc -l`
if [ $attempts -ne 0 ]
then
first_time=`grep $ip $FAILED_LOG | grep " $user " | head -1 | cut -c-16`
time="$first_time"
if [ $attempts -gt 1 ]
then
last_time=`grep $ip $FAILED_LOG | grep " $user " | tail -1 | cut -c-16`
time="$first_time -> $last_time"
fi
HOST=$(host $ip 8.8.8.8 | tail -1 | awk '{ print $NF }' )
printf "%-10s|%-10s|%-10s|%-15s|%-15s|%-s\n" "Failed" "$user" "$attempts" "$ip" "$HOST" "$time";
fi
done
done
for ip in $success_ip_list;
do
for user in $success_users;
do
# Count successful login attempts by this user from this IP
attempts=`grep $ip $SUCCESS_LOG | grep " $user " | wc -l`
if [ $attempts -ne 0 ]
then
first_time=`grep $ip $SUCCESS_LOG | grep " $user " | head -1 | cut -c-16`
time="$first_time"
if [ $attempts -gt 1 ]
then
last_time=`grep $ip $SUCCESS_LOG | grep " $user " | tail -1 | cut -c-16`
time="$first_time -> $last_time"
fi
HOST=$(host $ip 8.8.8.8 | tail -1 | awk '{ print $NF }' )
printf "%-10s|%-10s|%-10s|%-15s|%-15s|%-s\n" "Success" "$user" "$attempts" "$ip" "$HOST" "$time";
fi
done
done
rm -f $FAILED_LOG
rm -f $SUCCESS_LOG
Give executable permission to the script
# chmod u+x /tmp/intruder_detect.sh
Execute the script
# /tmp/intruder_detect.sh
Status |User |Attempts |IP address |Host |Time range
Failed |root |5 |192.168.0.102 |3(NXDOMAIN) |Jan 11 20:44:04 -> Jan 11 20:50:17
Failed |root |2 |192.168.0.106 |3(NXDOMAIN) |Jan 11 20:51:54 -> Jan 11 20:51:59
Success |root |1 |192.168.0.102 |3(NXDOMAIN) |Jan 11 20:50:26
Success |root |2 |192.168.0.106 |3(NXDOMAIN) |Jan 11 21:50:24 -> Jan 11 21:50:42
Based on these logs we can figure out that there were 5 failed login attempts from 192.168.0.102
, 2 failed login attempts from 192.168.0.106
Similarly there were couple of successful login attempts from 192.168.0.102
and 192.168.0.106
How the script works?
- The
intruder_detect.sh
script defaults to using/var/log/secure
as input. Alternatively, we can provide a log file with a command-line argument. - The successful and failed logins are collected in a temporary file to reduce processing.
- When a login attempt fails, SSH logs lines are similar to this:
sshd[3217]: Failed password for root from 192.168.0.102 port 53720 ssh2
- When a login is successful, SSH logs lines are similar to this
sshd[4881]: Accepted password for root from 192.168.0.106 port 49920 ssh2
- The script greps for the "
Failed passw
" string to get failed login attempt details and puts those lines in/tmp/failed.$$.log
- The script greps for "
Accepted password|Accepted publickey|keyboard-interactive
" string to get successful login attempts and puts those lines in/tmp/success.$$.log
- The
$$
in the temporary filename will be automatically replaced by the script's PID - The next step is to extract the users who successfully logged in and who failed to login.
The awk command extracts the fifth field from the end (the user name) and pipes that to sort anduniq
to create a list of the users. - Next, the unique IP addresses are extracted with a regular expression and the
egrep
command.
Nested for loops iterate through the IP address and users extracting the lines with each IP address and user combination. If the number of attempts for this IP/User combination is > 0, the time of the first occurrence is extracted with grep, head, and cut. If the number of attempts is > 1, then the last time is extracted using tail instead of head. - This login attempt is then reported with the formatted
printf
command. - Finally, the temporary file is removed.
Conclusion
In this tutorial we learned how we can check login history of users in Linux and consolidate the output based on the data available from /var/log/secure
. You could also setup audit to get the list of all login attempts from different Linux users.
Lastly I hope the steps from the article to check and monitor Linux login history was helpful. So, let me know your suggestions and feedback using the comment section.
References
I have used below external references for this tutorial guide
Script to check successful and failed login attempts in Linux
How can I set this to run every day/cron and save the log to a file? thanks.
Store
printf
content to a log file and run this script using cron. You can adjust the cron value based on your requirement. For example to run everyday at 8 AM use0 8 * * *
The script works well but a lot of lines with “grep: Invalid back reference” may be some problem with the version of egrep ? I’m on a Centos 7 machine
HOST=$(host $ip 8.8.8.8 | tail -1 | awk ‘{ print $NF }’ ) — please explain
This is to perform a IP to hostname lookup. We are using Google’s DNS to perform the lookup. Since my lab is in private network, I am getting NXDOMAIN, but if you do a lookup of a public IP then you should get proper hostname value. Or you can also use your own DNS (if any) to perform hostname lookup