LSMS/scripts/monitor_passwd.py

113 lines
3.1 KiB
Python
Executable File

#!/usr/bin/env python3
# written by sqall
# twitter: https://twitter.com/sqall01
# blog: https://h4des.org
# github: https://github.com/sqall01
#
# Licensed under the MIT License.
"""
Short summary:
Monitor /etc/passwd for changes to detect malicious attempts to hijack/change users.
NOTE: The first execution of this script will only show you the current state of the environment which should be acknowledged before monitoring for changes will become an effective security measure.
Requirements:
None
"""
import os
from typing import Dict
from lib.state import load_state, store_state
from lib.util import output_error, output_finding
from lib.util_user import get_system_users
# Read configuration.
try:
from config.config import ALERTR_FIFO, FROM_ADDR, TO_ADDR, STATE_DIR
from config.monitor_passwd import ACTIVATED
STATE_DIR = os.path.join(os.path.dirname(__file__), STATE_DIR, os.path.basename(__file__))
except:
ALERTR_FIFO = None
FROM_ADDR = None
TO_ADDR = None
ACTIVATED = True
STATE_DIR = os.path.join("/tmp", os.path.basename(__file__))
def _get_passwd() -> Dict[str, str]:
passwd_data = {}
for user_obj in get_system_users():
user = user_obj.name
passwd_data[user] = str(user_obj)
return passwd_data
def monitor_passwd():
# Decide where to output results.
print_output = False
if ALERTR_FIFO is None and FROM_ADDR is None and TO_ADDR is None:
print_output = True
if not ACTIVATED:
if print_output:
print("Module deactivated.")
return
stored_passwd_data = {}
try:
stored_passwd_data = load_state(STATE_DIR)
except Exception as e:
output_error(__file__, str(e))
return
curr_passwd_data = {}
try:
curr_passwd_data = _get_passwd()
except Exception as e:
output_error(__file__, str(e))
return
# Compare stored data with current one.
for stored_entry_user in stored_passwd_data.keys():
# Extract current entry belonging to the same user.
if stored_entry_user not in curr_passwd_data.keys():
message = "User '%s' was deleted." % stored_entry_user
output_finding(__file__, message)
continue
# Check entry was modified.
if stored_passwd_data[stored_entry_user] != curr_passwd_data[stored_entry_user]:
message = "Passwd entry for user '%s' was modified.\n\n" % stored_entry_user
message += "Old entry: %s\n" % stored_passwd_data[stored_entry_user]
message += "New entry: %s" % curr_passwd_data[stored_entry_user]
output_finding(__file__, message)
# Check new data was added.
for curr_entry_user in curr_passwd_data.keys():
if curr_entry_user not in stored_passwd_data.keys():
message = "User '%s' was added.\n\n" % curr_entry_user
message += "Entry: %s" % curr_passwd_data[curr_entry_user]
output_finding(__file__, message)
try:
store_state(STATE_DIR, curr_passwd_data)
except Exception as e:
output_error(__file__, str(e))
if __name__ == '__main__':
monitor_passwd()